From 0913e4d4ef12060afad5fdbdfd9ef717363c111b Mon Sep 17 00:00:00 2001 From: Tom Quirk <12551741+tomquirk@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:44:05 +1000 Subject: [PATCH] chore: nuke v3 upgrade funding cycle (#4248) --- .../AllocatorBadge.tsx | 0 src/components/announcements/Announcement.tsx | 20 + .../ExampleFeatureAnnouncement.tsx | 22 - .../shared/ProjectPayMods/ProjectModInput.tsx | 2 +- .../components/ProjectHeaderCountdown.tsx | 10 +- .../DetailsSection/DurationFields.tsx | 2 +- .../DurationInputAndSelect.tsx | 0 .../EditCollectionDetailsSection.tsx | 4 +- .../NftCollectionDetailsFormItems.tsx | 0 .../UpdateNftsPage}/formFields.ts | 0 .../EditNftsPage/hooks/useEditingNfts.ts | 2 +- .../LaunchFundingCycleForm.tsx | 212 ---------- .../UpgradeFundingCycle/NftSummarySection.tsx | 97 ----- .../ReconfigurePreview.tsx | 124 ------ .../SetNftOperatorPermissionsButton.tsx | 44 -- .../UpgradeFundingCycle.tsx | 51 --- .../hooks/useLaunchFundingCycles.ts | 69 --- .../hooks/useLaunchFundingCyclesData.ts | 103 ----- .../hooks/useLaunchNftFundingCycle.ts | 49 --- .../hooks/useLaunchStandardFundingCycle.ts | 23 - .../ProjectUpgradesPage/versions/upgrades.ts | 9 +- .../versions/useAvailableUpgrades.ts | 7 +- .../AllocatorFormItem.tsx | 31 -- .../DistributionSplitModal/AmountFormItem.tsx | 126 ------ .../DistributionSplitModal.tsx | 393 ------------------ .../PercentageFormItem.tsx | 63 --- .../V2V3ProjectPayoutFormItem.tsx | 40 -- .../DistributionSplitModal/types.ts | 12 - .../DistributionSplitModal/utils.ts | 8 - .../DrawerSection.tsx | 9 - .../FundingCycleDrawer.tsx | 24 -- .../FundingDrawer/FundingDrawer.tsx | 47 --- .../DistributionSplitsSection.tsx | 315 -------------- .../PayoutConfigurationExplainerCollapse.tsx | 33 -- .../SpecificLimitModal.tsx | 94 ----- .../FundingCycleExplainerCollapse.tsx | 33 -- .../FundingDrawer/FundingForm/FundingForm.tsx | 379 ----------------- .../AddNftsSection/AddNftsSection.tsx | 85 ---- .../AddNftsSection/hooks/useAddNfts.ts | 59 --- .../hooks/useSaveNewCollection.ts | 91 ---- .../NftDrawer/DangerZoneSection.tsx | 43 -- .../NftDrawer/NftDrawer.tsx | 82 ---- .../RulesDrawer/RulesDrawer.tsx | 41 -- .../RulesDrawer/RulesForm/RulesForm.tsx | 318 -------------- .../RulesForm/TokenMintingExtra.tsx | 20 - .../TokenDrawer/TokenDrawer.tsx | 59 --- .../TokenForm/MintRateFormItem.tsx | 43 -- .../TokenForm/ReservedTokensFormItem.tsx | 85 ---- .../TokenDrawer/TokenForm/TokenForm.tsx | 361 ---------------- .../hooks/useFundingCycleDrawer.ts | 33 -- .../SplitItem/JuiceboxProjectBeneficiary.tsx | 2 +- .../v2v3/shared/V2V3ProjectLink.tsx | 2 +- .../useLaunchFundingCyclesWithNftsTx.ts | 246 ----------- .../transactor/useLaunchProjectWithNftsTx.ts | 18 +- .../transactor/useLaunchFundingCyclesTx.ts | 84 ---- src/locales/messages.pot | 210 ---------- 56 files changed, 47 insertions(+), 4292 deletions(-) rename src/components/{v2v3/shared/FundingCycleConfigurationDrawers => }/AllocatorBadge.tsx (100%) delete mode 100644 src/components/announcements/ExampleFeatureAnnouncement.tsx rename src/components/v2v3/{shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm => V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/DetailsSection}/DurationInputAndSelect.tsx (100%) rename src/components/v2v3/{shared/FundingCycleConfigurationDrawers/NftDrawer/shared => V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage}/NftCollectionDetailsFormItems.tsx (100%) rename src/components/v2v3/{shared/FundingCycleConfigurationDrawers/NftDrawer/shared => V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage}/formFields.ts (100%) delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/LaunchFundingCycleForm.tsx delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/NftSummarySection.tsx delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/ReconfigurePreview.tsx delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/SetNftOperatorPermissionsButton.tsx delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/UpgradeFundingCycle.tsx delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCycles.ts delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCyclesData.ts delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchNftFundingCycle.ts delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchStandardFundingCycle.ts delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AllocatorFormItem.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AmountFormItem.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/DistributionSplitModal.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/PercentageFormItem.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/V2V3ProjectPayoutFormItem.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/types.ts delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/utils.ts delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/DrawerSection.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingCycleDrawer.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingDrawer.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/DistributionSplitsSection.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/PayoutConfigurationExplainerCollapse.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/SpecificLimitModal.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingCycleExplainerCollapse.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingForm.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/AddNftsSection.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useAddNfts.ts delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useSaveNewCollection.ts delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/DangerZoneSection.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/NftDrawer.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesDrawer.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/RulesForm.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/TokenMintingExtra.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenDrawer.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/MintRateFormItem.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/ReservedTokensFormItem.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/TokenForm.tsx delete mode 100644 src/components/v2v3/shared/FundingCycleConfigurationDrawers/hooks/useFundingCycleDrawer.ts delete mode 100644 src/hooks/JB721Delegate/transactor/useLaunchFundingCyclesWithNftsTx.ts delete mode 100644 src/hooks/v2v3/transactor/useLaunchFundingCyclesTx.ts diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/AllocatorBadge.tsx b/src/components/AllocatorBadge.tsx similarity index 100% rename from src/components/v2v3/shared/FundingCycleConfigurationDrawers/AllocatorBadge.tsx rename to src/components/AllocatorBadge.tsx diff --git a/src/components/announcements/Announcement.tsx b/src/components/announcements/Announcement.tsx index b1cb838786..4c9e9a4b84 100644 --- a/src/components/announcements/Announcement.tsx +++ b/src/components/announcements/Announcement.tsx @@ -15,6 +15,26 @@ export function getCompleted() { return (content ? JSON.parse(content) : {}) as { [k: string]: true } } +/** + * + * @example + export const ExampleFeatureAnnouncement = ( + props: Pick, +) => { + return ( + +

This is an example feature announcement.

+

Use me as a base!

+
+ ) +} + */ export const Announcement: React.FC< React.PropsWithChildren > = props => { diff --git a/src/components/announcements/ExampleFeatureAnnouncement.tsx b/src/components/announcements/ExampleFeatureAnnouncement.tsx deleted file mode 100644 index 85edc878f7..0000000000 --- a/src/components/announcements/ExampleFeatureAnnouncement.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { JuiceModalProps } from 'components/modals/JuiceModal' -import { NewFeatureAnnouncement } from './NewFeatureAnnouncement' - -/** - * An example of a feature announcement. - */ -export const ExampleFeatureAnnouncement = ( - props: Pick, -) => { - return ( - -

This is an example feature announcement.

-

Use me as a base!

-
- ) -} diff --git a/src/components/v1/shared/ProjectPayMods/ProjectModInput.tsx b/src/components/v1/shared/ProjectPayMods/ProjectModInput.tsx index aea03c0f68..be876cf4eb 100644 --- a/src/components/v1/shared/ProjectPayMods/ProjectModInput.tsx +++ b/src/components/v1/shared/ProjectPayMods/ProjectModInput.tsx @@ -1,8 +1,8 @@ import { CloseCircleOutlined, LockOutlined } from '@ant-design/icons' import { Button, Col, Row, Space } from 'antd' +import { AllocatorBadge } from 'components/AllocatorBadge' import EthereumAddress from 'components/EthereumAddress' import CurrencySymbol from 'components/currency/CurrencySymbol' -import { AllocatorBadge } from 'components/v2v3/shared/FundingCycleConfigurationDrawers/AllocatorBadge' import { NULL_ALLOCATOR_ADDRESS, V1_V3_ALLOCATOR_ADDRESS, diff --git a/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeaderCountdown.tsx b/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeaderCountdown.tsx index 6daa556495..6ab5891b81 100644 --- a/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeaderCountdown.tsx +++ b/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeaderCountdown.tsx @@ -5,7 +5,7 @@ import { useFundingCycleCountdown } from '../hooks/useFundingCycleCountdown' const RS_PROJECT_ID = 618 -export type ProjectHeaderCountdownProps = { +type ProjectHeaderCountdownProps = { className?: string } @@ -45,13 +45,7 @@ export const ProjectHeaderCountdown: React.FC = ({ ) } -export const CountdownCard = ({ - label, - unit, -}: { - label: ReactNode - unit: number -}) => ( +const CountdownCard = ({ label, unit }: { label: ReactNode; unit: number }) => (
{unit}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/DetailsSection/DurationFields.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/DetailsSection/DurationFields.tsx index 7e3d2b7596..236669a547 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/DetailsSection/DurationFields.tsx +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/DetailsSection/DurationFields.tsx @@ -3,9 +3,9 @@ import TooltipLabel from 'components/TooltipLabel' import { durationOptions } from 'components/inputs/DurationInput' import { JuiceSwitch } from 'components/inputs/JuiceSwitch' import { CYCLE_EXPLANATION } from 'components/strings' -import DurationInputAndSelect from 'components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DurationInputAndSelect' import { useState } from 'react' import { useEditCycleFormContext } from '../EditCycleFormContext' +import DurationInputAndSelect from './DurationInputAndSelect' export function DurationFields() { const { editCycleForm, initialFormData } = useEditCycleFormContext() diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DurationInputAndSelect.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/DetailsSection/DurationInputAndSelect.tsx similarity index 100% rename from src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DurationInputAndSelect.tsx rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/DetailsSection/DurationInputAndSelect.tsx diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/EditCollectionDetailsSection.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/EditCollectionDetailsSection.tsx index 735a9b99fb..4a04653796 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/EditCollectionDetailsSection.tsx +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/EditCollectionDetailsSection.tsx @@ -8,8 +8,8 @@ import { useNftCollectionMetadata } from 'hooks/JB721Delegate/useNftCollectionMe import { useReconfigureNftCollectionMetadata } from 'hooks/v2v3/transactor/useReconfigureNftCollectionMetadata' import { NftCollectionMetadata } from 'models/nftRewards' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { NftCollectionDetailsFormItems } from '../../../../../shared/FundingCycleConfigurationDrawers/NftDrawer/shared/NftCollectionDetailsFormItems' -import { MarketplaceFormFields } from '../../../../../shared/FundingCycleConfigurationDrawers/NftDrawer/shared/formFields' +import { NftCollectionDetailsFormItems } from './NftCollectionDetailsFormItems' +import { MarketplaceFormFields } from './formFields' const useCollectionDetailsForm = () => { const [form] = useForm() diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/shared/NftCollectionDetailsFormItems.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/NftCollectionDetailsFormItems.tsx similarity index 100% rename from src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/shared/NftCollectionDetailsFormItems.tsx rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/NftCollectionDetailsFormItems.tsx diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/shared/formFields.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/formFields.ts similarity index 100% rename from src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/shared/formFields.ts rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/formFields.ts diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.ts index ccee485753..015add94e0 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.ts +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.ts @@ -1,5 +1,5 @@ import { useForm } from 'antd/lib/form/Form' -import { MarketplaceFormFields } from 'components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/shared/formFields' +import { MarketplaceFormFields } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/UpdateNftsPage/formFields' import { NftRewardsContext } from 'contexts/NftRewards/NftRewardsContext' import { NftRewardTier } from 'models/nftRewards' import { useContext, useEffect, useState } from 'react' diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/LaunchFundingCycleForm.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/LaunchFundingCycleForm.tsx deleted file mode 100644 index 46e9a2f44d..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/LaunchFundingCycleForm.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Button, Divider, Form, Input } from 'antd' -import RichButton from 'components/buttons/RichButton' -import { Callout } from 'components/Callout/Callout' -import { useIsNftProject } from 'components/Create/hooks/DeployProject/hooks/NFT/useIsNftProject' -import { JuiceSwitch } from 'components/inputs/JuiceSwitch' -import UnsavedChangesModal from 'components/modals/UnsavedChangesModal' -import { FundingDrawer } from 'components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingDrawer' -import { NftDrawer } from 'components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/NftDrawer' -import { RulesDrawer } from 'components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesDrawer' -import { TokenDrawer } from 'components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenDrawer' -import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { useNftDeployerCanReconfigure } from 'hooks/JB721Delegate/contractReader/useNftDeployerCanReconfigure' -import { useContext, useState } from 'react' -import { useDispatch } from 'react-redux' -import { editingV2ProjectActions } from 'redux/slices/editingV2Project' -import { formatDate } from 'utils/format/formatDate' -import { useEditingFundingCycleConfig } from '../../../../../../hooks/useEditingFundingCycleConfig' -import { useLaunchFundingCycles } from './hooks/useLaunchFundingCycles' -import { ReconfigurePreview } from './ReconfigurePreview' -import { SetNftOperatorPermissionsButton } from './SetNftOperatorPermissionsButton' - -export function LaunchFundingCycleForm() { - const { projectOwnerAddress, fundingCycle } = useContext(V2V3ProjectContext) - const { projectId } = useContext(ProjectMetadataContext) - - const [fundingDrawerVisible, setFundingDrawerVisible] = - useState(false) - const [tokenDrawerVisible, setTokenDrawerVisible] = useState(false) - const [rulesDrawerVisible, setRulesDrawerVisible] = useState(false) - const [nftDrawerVisible, setNftDrawerVisible] = useState(false) - const [unsavedChangesModalVisibile, setUnsavedChangesModalVisible] = - useState(false) - const [nftOperatorConfirmed, setNftOperatorConfirmed] = useState() - - const [syncStartTime, setSyncStartTime] = useState(true) - - const dispatch = useDispatch() - const editingFundingCycleConfig = useEditingFundingCycleConfig() - const { launchFundingCycleLoading, launchFundingCycle } = - useLaunchFundingCycles({ editingFundingCycleConfig }) - - const isNftFundingCycle = useIsNftProject() - const nftDeployerCanReconfigure = useNftDeployerCanReconfigure({ - projectId, - projectOwnerAddress, - }) - - const closeReconfigureDrawer = () => { - setFundingDrawerVisible(false) - setTokenDrawerVisible(false) - setNftDrawerVisible(false) - setRulesDrawerVisible(false) - } - const closeUnsavedChangesModal = () => setUnsavedChangesModalVisible(false) - const closeUnsavedChangesModalAndExit = () => { - closeUnsavedChangesModal() - } - - const defaultV3StartTime = fundingCycle?.start.add(fundingCycle.duration) - - return ( - <> -
-
- - Start the V3 cycle as soon as the current V2 cycle ends{' '} - - ( - {defaultV3StartTime - ? formatDate(defaultV3StartTime?.mul(1000)) - : '--'} - ) - - . - - } - > - - - {syncStartTime ? null : ( - Start time} - extra={ - - Unix timestamp in seconds. Leave blank to start immediately. - - } - className={'mt-5'} - > - { - const time = e.target.value - dispatch(editingV2ProjectActions.setMustStartAtOrAfter(time)) - }} - /> - - )} -
- setFundingDrawerVisible(true)} - /> - setTokenDrawerVisible(true)} - /> - setNftDrawerVisible(true)} - /> - setRulesDrawerVisible(true)} - /> - - - -

- Review and deploy -

-
- -
- - {isNftFundingCycle && !nftDeployerCanReconfigure ? ( -
- - - You're about to add NFTs to your cycle. You'll need to{' '} - grant NFT permissions before launching cycles. - - -
- setNftOperatorConfirmed(true)} - size="large" - /> - - -
-
- ) : ( - - )} -
- - - - - - - - ) -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/NftSummarySection.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/NftSummarySection.tsx deleted file mode 100644 index 9d0e3661bc..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/NftSummarySection.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Col, Row } from 'antd' -import ExternalLink from 'components/ExternalLink' -import { JuiceVideoThumbnailOrImage } from 'components/JuiceVideo/JuiceVideoThumbnailOrImage' -import { MinimalCollapse } from 'components/MinimalCollapse' -import Paragraph from 'components/Paragraph' -import { DEFAULT_NFT_MAX_SUPPLY } from 'constants/nftRewards' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { useContext } from 'react' -import { useAppSelector } from 'redux/hooks/useAppSelector' -import { isZeroAddress } from 'utils/address' -import { classNames } from 'utils/classNames' - -export default function NftSummarySection() { - const { fundingCycleMetadata } = useContext(V2V3ProjectContext) - const { - nftRewards: { rewardTiers }, - fundingCycleMetadata: { dataSource: newDataSource }, - } = useAppSelector(state => state.editingV2Project) - - const removedDataSource = - isZeroAddress(newDataSource) && - fundingCycleMetadata?.dataSource && - !isZeroAddress(fundingCycleMetadata?.dataSource) - - return ( -
- - {removedDataSource && NFTs detached from project.} - {!removedDataSource && - rewardTiers?.map((rewardTier, index) => ( - - - - - - - {rewardTier.name} - -

- - Contribution floor:{' '} - {rewardTier.contributionFloor} ETH - -

- {rewardTier.maxSupply && - rewardTier.maxSupply !== DEFAULT_NFT_MAX_SUPPLY ? ( - - - Max. supply:{' '} - {rewardTier.maxSupply} - - - ) : null} - {rewardTier.externalLink && ( - - - Website:{' '} - - {rewardTier.externalLink} - - - - )} - - - {rewardTier.description && ( -
- - Description: - - -
- )} - -
- ))} -
-
- ) -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/ReconfigurePreview.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/ReconfigurePreview.tsx deleted file mode 100644 index b95386990e..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/ReconfigurePreview.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { MinimalCollapse } from 'components/MinimalCollapse' -import DiffedSplitList from 'components/v2v3/shared/DiffedSplits/DiffedSplitList' -import FundingCycleDetails from 'components/v2v3/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { BigNumber } from 'ethers' -import { NftRewardTier } from 'models/nftRewards' -import { Split } from 'models/splits' -import { - V2V3FundAccessConstraint, - V2V3FundingCycle, - V2V3FundingCycleData, - V2V3FundingCycleMetadata, -} from 'models/v2v3/fundingCycle' -import { useContext } from 'react' - -import { - deriveNextIssuanceRate, - getDefaultFundAccessConstraint, -} from 'utils/v2v3/fundingCycle' -import { formatReservedRate } from 'utils/v2v3/math' - -import NftSummarySection from './NftSummarySection' - -export function ReconfigurePreview({ - payoutSplits, - reserveSplits, - fundingCycleMetadata, - fundingCycleData, - fundAccessConstraints, - nftRewards, - mustStartAtOrAfter, -}: { - payoutSplits: Split[] - reserveSplits: Split[] - fundingCycleMetadata: V2V3FundingCycleMetadata - fundingCycleData: V2V3FundingCycleData - fundAccessConstraints: V2V3FundAccessConstraint[] - nftRewards?: NftRewardTier[] - mustStartAtOrAfter?: string -}) { - const { - fundingCycle: currentFC, - payoutSplits: currentPayoutSplits, - reservedTokensSplits: currentReserveSplits, - } = useContext(V2V3ProjectContext) - - const fundingCycle: V2V3FundingCycle = { - ...fundingCycleData, - number: BigNumber.from(1), - configuration: BigNumber.from(0), - basedOn: BigNumber.from(0), - start: mustStartAtOrAfter - ? BigNumber.from(mustStartAtOrAfter) - : BigNumber.from(Date.now()).div(1000), - metadata: BigNumber.from(0), - weight: deriveNextIssuanceRate({ - weight: fundingCycleData.weight, - previousFC: currentFC, - }), - } - - const fundAccessConstraint = getDefaultFundAccessConstraint( - fundAccessConstraints, - ) - - const distributionLimit = fundAccessConstraint?.distributionLimit - const distributionLimitCurrency = - fundAccessConstraint?.distributionLimitCurrency - - const formattedReservedRate = parseFloat( - formatReservedRate(fundingCycleMetadata.reservedRate), - ) - - return ( -
e.stopPropagation()} - > - - - - - {distributionLimit?.gt(0) ? ( - - ) : ( - - No distributions configured. - - )} - - - {fundingCycleMetadata.reservedRate?.gt(0) ? ( - - ) : ( - - No reserved tokens configured. - - )} - - - {nftRewards ? : null} -
- ) -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/SetNftOperatorPermissionsButton.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/SetNftOperatorPermissionsButton.tsx deleted file mode 100644 index 2a19043362..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/SetNftOperatorPermissionsButton.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { CheckCircleOutlined } from '@ant-design/icons' -import { Trans } from '@lingui/macro' -import { Button, ButtonProps } from 'antd' -import { useSetNftOperatorPermissionsTx } from 'hooks/JB721Delegate/transactor/useSetNftOperatorPermissionsTx' -import { useState } from 'react' - -export function SetNftOperatorPermissionsButton({ - onConfirmed, - ...props -}: { - onConfirmed: VoidFunction -} & ButtonProps) { - const [loading, setLoading] = useState(false) - const [txExecuted, setTxExecuted] = useState(false) - - const setNftOperatorPermissionsTx = useSetNftOperatorPermissionsTx() - - const setPermissions = async () => { - setLoading(true) - await setNftOperatorPermissionsTx(undefined, { - onConfirmed: () => { - setTxExecuted(true) - setLoading(false) - onConfirmed() - }, - }) - } - - return ( - - ) -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/UpgradeFundingCycle.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/UpgradeFundingCycle.tsx deleted file mode 100644 index f462770942..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/UpgradeFundingCycle.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Callout } from 'components/Callout/Callout' -import { CV_V3 } from 'constants/cv' -import { V2V3ContractsProvider } from 'contexts/v2v3/Contracts/V2V3ContractsProvider' -import { useRef } from 'react' -import { Provider } from 'react-redux' -import store, { createStore } from 'redux/store' -import { useInitialEditingData } from '../../../../../../hooks/useInitialEditingData' -import { LaunchFundingCycleForm } from './LaunchFundingCycleForm' - -/** - * Form to relaunch a V2 funding cycle on V3. - */ -function RelaunchV2FundingCycleForm() { - // load the initial (presumed V2) funding cycle config. - useInitialEditingData({ visible: true }) - - return -} - -export function UpgradeFundingCycle() { - const localStoreRef = useRef() - if (!localStoreRef.current) { - localStoreRef.current = createStore() - } - - return ( - <> - {localStoreRef.current && ( - - -
-

- Launch V3 funding cycle -

-

- You currently have a cycle on Juicebox V2. Use the form below to - relaunch your cycle on Juicebox V3. -

- - - Your V2 cycle rules have been prefilled. - - - -
-
-
- )} - - ) -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCycles.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCycles.ts deleted file mode 100644 index 62f4c52056..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCycles.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { useIsNftProject } from 'components/Create/hooks/DeployProject/hooks/NFT/useIsNftProject' -import { EditingFundingCycleConfig } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig' -import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext' -import { revalidateProject } from 'lib/api/nextjs' -import { PV2 } from 'models/pv' -import { useCallback, useContext, useState } from 'react' -import { emitErrorNotification } from 'utils/notifications' -import { reloadWindow } from 'utils/windowUtils' -import { useLaunchNftFundingCycles } from './useLaunchNftFundingCycle' -import { useLaunchStandardFundingCycles } from './useLaunchStandardFundingCycle' - -export const useLaunchFundingCycles = ({ - editingFundingCycleConfig, -}: { - editingFundingCycleConfig: EditingFundingCycleConfig -}) => { - const { projectId, pv } = useContext(ProjectMetadataContext) - - const [launchFundingCycleTxLoading, setLaunchFundingCycleTxLoading] = - useState(false) - - const launchStandardFundingCycles = useLaunchStandardFundingCycles( - editingFundingCycleConfig, - ) - const launchNftFundingCycles = useLaunchNftFundingCycles( - editingFundingCycleConfig, - ) - const isNftFundingCycle = useIsNftProject() - - const launchFundingCycle = useCallback(async () => { - setLaunchFundingCycleTxLoading(true) - - const callbacks = { - onConfirmed() { - setLaunchFundingCycleTxLoading(false) - - if (projectId) { - revalidateProject({ - pv: pv as PV2, - projectId: String(projectId), - }) - } - - // reload window to force-reflect latest changes - reloadWindow() - }, - } - - const txSuccessful = isNftFundingCycle - ? await launchNftFundingCycles(callbacks) - : await launchStandardFundingCycles(callbacks) - - if (!txSuccessful) { - setLaunchFundingCycleTxLoading(false) - emitErrorNotification('Failed to launch cycles.') - } - }, [ - launchStandardFundingCycles, - launchNftFundingCycles, - isNftFundingCycle, - projectId, - pv, - ]) - - return { - launchFundingCycleLoading: launchFundingCycleTxLoading, - launchFundingCycle, - } -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCyclesData.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCyclesData.ts deleted file mode 100644 index bd5a69385a..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchFundingCyclesData.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { EditingFundingCycleConfig } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig' -import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { BigNumber } from 'ethers' -import { useDefaultJBETHPaymentTerminal } from 'hooks/defaultContracts/useDefaultJBETHPaymentTerminal' -import { LaunchFundingCyclesData } from 'hooks/v2v3/transactor/useLaunchFundingCyclesTx' -import { V3FundingCycleMetadata } from 'models/v3/fundingCycle' -import { useContext } from 'react' - -/** - * Transforms given launch data to ensure V3 compatibility. - */ -const transformLaunchDataForV3 = ( - launchData: LaunchFundingCyclesData, - V3JBETHPaymentTerminalAddress: string, -): LaunchFundingCyclesData => { - // add in new V3 properties - const v3FundingCycleMetadata: V3FundingCycleMetadata = { - ...launchData.fundingCycleMetadata, - global: { - ...launchData.fundingCycleMetadata.global, - pauseTransfers: false, - }, - preferClaimedTokenOverride: false, - metadata: BigNumber.from(0), - } - - // make the fund access constraints uses the V3 terminal. - // Assumes there's only one constraint, which is the case for now. - const v3FundAccessConstraints = [ - { - ...launchData.fundAccessConstraints[0], - terminal: V3JBETHPaymentTerminalAddress, - }, - ] - - const newLaunchData = { - ...launchData, - fundingCycleMetadata: v3FundingCycleMetadata, - fundAccessConstraints: v3FundAccessConstraints, - } - - return newLaunchData -} - -export function useLaunchFundingCyclesData({ - editingFundingCycleConfig, -}: { - editingFundingCycleConfig: EditingFundingCycleConfig -}) { - const { fundingCycle } = useContext(V2V3ProjectContext) - const { projectId, pv } = useContext(ProjectMetadataContext) - const defaultJBETHPaymentTerminal = useDefaultJBETHPaymentTerminal() - - const { - editingPayoutGroupedSplits, - editingReservedTokensGroupedSplits, - editingFundingCycleMetadata, - editingFundingCycleData, - editingFundAccessConstraints, - editingMustStartAtOrAfter, - } = editingFundingCycleConfig - - if ( - !( - fundingCycle && - editingFundingCycleData && - editingFundingCycleMetadata && - editingFundAccessConstraints && - projectId && - pv && - defaultJBETHPaymentTerminal - ) - ) { - return - } - - // set new v3 funding cycle start to be: - // `current V2 FC start time + `current V2 FC duration` - const newStart = fundingCycle.start.add(fundingCycle.duration) - - const initialLaunchData = { - projectId, - fundingCycleData: editingFundingCycleData, - fundingCycleMetadata: { - ...editingFundingCycleMetadata, - start: newStart, - }, - fundAccessConstraints: editingFundAccessConstraints, - groupedSplits: [ - editingPayoutGroupedSplits, - editingReservedTokensGroupedSplits, - ], - mustStartAtOrAfter: editingMustStartAtOrAfter, - } - - const launchData = transformLaunchDataForV3( - initialLaunchData, - defaultJBETHPaymentTerminal.address, - ) - - return launchData -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchNftFundingCycle.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchNftFundingCycle.ts deleted file mode 100644 index c24639acf9..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchNftFundingCycle.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { EditingFundingCycleConfig } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig' -import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext' -import { useLaunchFundingCyclesWithNftsTx } from 'hooks/JB721Delegate/transactor/useLaunchFundingCyclesWithNftsTx' -import { LaunchFundingCyclesData } from 'hooks/v2v3/transactor/useLaunchFundingCyclesTx' -import { TransactionCallbacks } from 'models/transaction' -import { useContext } from 'react' -import { NFT_FUNDING_CYCLE_METADATA_OVERRIDES } from 'utils/nftFundingCycleMetadataOverrides' -import { useLaunchFundingCyclesData } from './useLaunchFundingCyclesData' - -function transformLaunchDataForNfts( - launchData: LaunchFundingCyclesData, -): LaunchFundingCyclesData { - return { - ...launchData, - fundingCycleMetadata: { - ...launchData?.fundingCycleMetadata, - ...NFT_FUNDING_CYCLE_METADATA_OVERRIDES, - }, - } -} - -export function useLaunchNftFundingCycles( - editingFundingCycleConfig: EditingFundingCycleConfig, -) { - const { projectId } = useContext(ProjectMetadataContext) - - const launchFundingCyclesWithNfts = useLaunchFundingCyclesWithNftsTx() - const launchData = useLaunchFundingCyclesData({ editingFundingCycleConfig }) - const launchNftFundingCyclesData = launchData - ? transformLaunchDataForNfts(launchData) - : undefined - - const { editingNftRewards } = editingFundingCycleConfig - - return (callbacks: TransactionCallbacks) => { - if (!launchNftFundingCyclesData || !editingNftRewards || !projectId) { - throw new Error('Failed to launch funding cycle. Some data was missing.') - } - - return launchFundingCyclesWithNfts( - { - projectId, - launchFundingCyclesData: launchNftFundingCyclesData, - tiered721DelegateData: editingNftRewards, - }, - callbacks, - ) - } -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchStandardFundingCycle.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchStandardFundingCycle.ts deleted file mode 100644 index 74bd5a5cfd..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgradeForms/V3/UpgradeFundingCycle/hooks/useLaunchStandardFundingCycle.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { EditingFundingCycleConfig } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig' -import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext' -import { useLaunchFundingCyclesTx } from 'hooks/v2v3/transactor/useLaunchFundingCyclesTx' -import { TransactionCallbacks } from 'models/transaction' -import { useContext } from 'react' -import { useLaunchFundingCyclesData } from './useLaunchFundingCyclesData' - -export function useLaunchStandardFundingCycles( - editingFundingCycleConfig: EditingFundingCycleConfig, -) { - const { projectId } = useContext(ProjectMetadataContext) - - const launchData = useLaunchFundingCyclesData({ editingFundingCycleConfig }) - const launchFundingCycles = useLaunchFundingCyclesTx() - - return (callbacks: TransactionCallbacks) => { - if (!launchData || !projectId) { - throw new Error('Failed to launch funding cycle. Some data was missing.') - } - - return launchFundingCycles({ projectId, ...launchData }, callbacks) - } -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgrades.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgrades.ts index 640b603783..4e4998d62c 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgrades.ts +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/upgrades.ts @@ -1,10 +1,7 @@ import { t } from '@lingui/macro' import { UpgradeController } from './upgradeForms/JBController3_1/UpgradeController' -import { UpgradeFundingCycle } from './upgradeForms/V3/UpgradeFundingCycle/UpgradeFundingCycle' -export type JBUpgrade = - | '3' // upgrades to layer 2 contracts - | '3_1' // upgrades to a new JBController +export type JBUpgrade = '3_1' // upgrades to a new JBController export const UPGRADES: { [k in JBUpgrade]: { @@ -13,10 +10,6 @@ export const UPGRADES: { component: () => JSX.Element | null } } = { - '3': { - name: 'Juicebox v3', - component: UpgradeFundingCycle, - }, '3_1': { name: 'Juicebox v3.1', description: () => diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/useAvailableUpgrades.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/useAvailableUpgrades.ts index 5c066981a6..8b32be24d0 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/useAvailableUpgrades.ts +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ProjectUpgradesPage/versions/useAvailableUpgrades.ts @@ -1,4 +1,3 @@ -import { CV_V3 } from 'constants/cv' import { V2V3ContractsContext } from 'contexts/v2v3/Contracts/V2V3ContractsContext' import { V2V3ProjectContractsContext } from 'contexts/v2v3/ProjectContracts/V2V3ProjectContractsContext' import { useContext } from 'react' @@ -6,15 +5,11 @@ import { isEqualAddress } from 'utils/address' import { JBUpgrade } from './upgrades' export function useAvailableUpgrades(): JBUpgrade[] | undefined { - const { cvs, contracts } = useContext(V2V3ContractsContext) + const { contracts } = useContext(V2V3ContractsContext) const { contracts: projectContracts } = useContext( V2V3ProjectContractsContext, ) - if (!cvs?.includes(CV_V3)) { - return ['3'] - } - if ( projectContracts?.JBController && projectContracts?.JBETHPaymentTerminal && diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AllocatorFormItem.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AllocatorFormItem.tsx deleted file mode 100644 index 3ce02b7790..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AllocatorFormItem.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Form, Radio } from 'antd' -import { - V2_V3_ALLOCATOR_ADDRESS, - NULL_ALLOCATOR_ADDRESS, -} from 'constants/contracts/mainnet/Allocators' -import { CV_V2 } from 'constants/cv' -import { V2V3ContractsContext } from 'contexts/v2v3/Contracts/V2V3ContractsContext' -import { useContext } from 'react' - -export function AllocatorFormItem() { - const { cv } = useContext(V2V3ContractsContext) - - if (cv !== CV_V2) return null - return ( - - - - V2 - - - V3 - - - - ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AmountFormItem.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AmountFormItem.tsx deleted file mode 100644 index 0ce24e210f..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/AmountFormItem.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Form, FormInstance } from 'antd' -import InputAccessoryButton from 'components/buttons/InputAccessoryButton' -import CurrencySwitch from 'components/currency/CurrencySwitch' -import CurrencySymbol from 'components/currency/CurrencySymbol' -import { FeeTooltipLabel } from 'components/FeeTooltipLabel' -import FormattedNumberInput from 'components/inputs/FormattedNumberInput' -import TooltipIcon from 'components/TooltipIcon' -import { CurrencyName } from 'constants/currency' -import { BigNumber } from 'ethers' -import { parseWad, stripCommas } from 'utils/format/formatNumber' -import { V2V3_CURRENCY_ETH, V2V3_CURRENCY_USD } from 'utils/v2v3/currency' -import { AddOrEditSplitFormFields, SplitType } from './types' -import { percentageValidator } from './utils' - -export function AmountFormItem({ - form, - distributionLimit, - distributionType, - fee, - editingSplitType, - currencyName, - isFirstSplit, - onCurrencyChange, -}: { - form: FormInstance - distributionType: 'amount' | 'percent' | 'both' - isEditPayoutPage?: boolean - distributionLimit?: string - fee: BigNumber | undefined - editingSplitType: SplitType - currencyName: CurrencyName - isFirstSplit: boolean - onCurrencyChange?: (currencyName: CurrencyName) => void -}) { - const amount = Form.useWatch('amount', form) - - function AfterFeeMessage() { - if (!fee || !amount || amount === '0') return null - - try { - return ( - - ) - } catch (e) { - console.error(e) - return null - } - } - - return ( - - {editingSplitType === 'address' ? ( -
- -
- ) : ( - - Payouts to other Juicebox projects won't incur fees. - - )} - - ) : null - } - > -
- - - ) : ( - - ) - } - /> - - {distributionType === 'amount' ? ( -
- {form.getFieldValue('percent') ?? '0'}% - - If you don't raise the sum of your payouts ( - - {distributionLimit}), this address will receive{' '} - {form.getFieldValue('percent')}% of all the ETH you raise. - - } - placement={'topLeft'} - iconClassName={'ml-1'} - /> -
- ) : null} -
-
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/DistributionSplitModal.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/DistributionSplitModal.tsx deleted file mode 100644 index dc5525e61c..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/DistributionSplitModal.tsx +++ /dev/null @@ -1,393 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { DatePicker, Form, Modal, Radio } from 'antd' -import { useForm, useWatch } from 'antd/lib/form/Form' -import { ModalMode, validateEthAddress } from 'components/formItems/formHelpers' -import { EthAddressInput } from 'components/inputs/EthAddressInput' -import { LOCKED_PAYOUT_EXPLANATION } from 'components/strings' -import { NULL_ALLOCATOR_ADDRESS } from 'constants/contracts/mainnet/Allocators' -import { CurrencyName } from 'constants/currency' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { BigNumber, constants, utils } from 'ethers' -import findIndex from 'lodash/findIndex' -import { Split } from 'models/splits' -import moment, * as Moment from 'moment' -import { useContext, useEffect, useMemo, useState } from 'react' -import { parseWad, stripCommas } from 'utils/format/formatNumber' -import { - adjustedSplitPercents, - amountFromPercent, - getDistributionPercentFromAmount, - getNewDistributionLimit, -} from 'utils/v2v3/distributions' -import { - MAX_DISTRIBUTION_LIMIT, - preciseFormatSplitPercent, - splitPercentFrom, -} from 'utils/v2v3/math' -import { AmountFormItem } from './AmountFormItem' -import { PercentageFormItem } from './PercentageFormItem' -import { AddOrEditSplitFormFields, SplitType } from './types' -import { V2V3ProjectPayoutFormItem } from './V2V3ProjectPayoutFormItem' - -type DistributionType = 'amount' | 'percent' | 'both' - -// Using both state and a form in this modal. I know it seems over the top, -// but the state is necessary to link the percent and amount fields, and the form -// is useful for its features such as field validation. -export function DistributionSplitModal({ - open, - mode, - isEditPayoutPage = false, - splits, // Locked and editable splits - editingSplit, // Split that is currently being edited (only in the case mode ==='Edit') - onSplitsChanged, - distributionLimit, - setDistributionLimit, - onClose, - currencyName, - onCurrencyChange, -}: { - open: boolean - mode: ModalMode // 'Add' or 'Edit' or 'Undefined' - overrideDistTypeWithPercentage?: boolean - isEditPayoutPage?: boolean - splits: Split[] - editingSplit?: Split - onSplitsChanged?: (splits: Split[]) => void - distributionLimit?: string - setDistributionLimit?: (distributionLimit: string) => void - onClose: VoidFunction - currencyName: CurrencyName - onCurrencyChange?: (currencyName: CurrencyName) => void -}) { - const { primaryETHTerminalFee } = useContext(V2V3ProjectContext) - - const [form] = useForm() - const amount = Form.useWatch('amount', form) - - const distributionLimitIsInfinite = useMemo( - () => - !distributionLimit || - parseWad(distributionLimit).eq(MAX_DISTRIBUTION_LIMIT), - [distributionLimit], - ) - - // true if no splits have been created, or only one split and it is selected - const isFirstSplit = useMemo( - () => !splits.length || (splits.length === 1 && editingSplit !== undefined), - [editingSplit, splits.length], - ) - - const [editingSplitType, setEditingSplitType] = useState('address') - const [distributionType, setDistributionType] = useState( - distributionLimitIsInfinite ? 'percent' : 'amount', - ) - const [projectId, setProjectId] = useState() - const [newDistributionLimit, setNewDistributionLimit] = useState() - const [lockedUntil, setLockedUntil] = useState< - Moment.Moment | undefined | null - >() - - useEffect(() => - form.setFieldsValue({ - projectId, - lockedUntil, - }), - ) - - // Set address project id to undefined if editing type is address - useEffect(() => { - if (editingSplitType === 'address') { - form.setFieldsValue({ projectId: undefined }) - setProjectId(undefined) - } - }, [editingSplitType, form]) - - useEffect(() => { - if (isEditPayoutPage && parseWad(distributionLimit).gt(0)) { - setDistributionType('both') - return - } else if (isEditPayoutPage && parseWad(distributionLimit).eq(0)) { - setDistributionType('percent') - return - } - setDistributionType(distributionLimitIsInfinite ? 'percent' : 'amount') - }, [distributionLimit, distributionLimitIsInfinite, open, isEditPayoutPage]) - - // Set the initial info for form from split - // If editing, format the lockedUntil and projectId - useEffect(() => { - if (!editingSplit) return - setEditingSplitType('address') - const isEditingProjectSplit = - editingSplit.projectId && - !BigNumber.from(editingSplit.projectId).eq( - BigNumber.from(constants.AddressZero), - ) - if (isEditingProjectSplit) { - setEditingSplitType('project') - setProjectId(parseInt(editingSplit.projectId ?? '').toString()) - } - setLockedUntil( - editingSplit.lockedUntil - ? Moment.default(editingSplit.lockedUntil * 1000) - : undefined, - ) - form.setFieldsValue({ - beneficiary: editingSplit.beneficiary, - percent: preciseFormatSplitPercent(editingSplit.percent), - allocator: editingSplit.allocator, - }) - - if (distributionLimitIsInfinite) { - const newAmount = amountFromPercent({ - percent: preciseFormatSplitPercent(editingSplit.percent), - amount: distributionLimit ?? '0', - }) - form.setFieldsValue({ amount: newAmount.toString() }) - } else if (distributionLimit) { - const percentPerBillion = editingSplit.percent - const newAmount = amountFromPercent({ - percent: preciseFormatSplitPercent(percentPerBillion), - amount: distributionLimit, - }) - form.setFieldsValue({ amount: newAmount.toString() }) - } else { - form.setFieldsValue({ amount: undefined }) - } - }, [distributionLimit, distributionLimitIsInfinite, editingSplit, form, open]) - - const resetStates = () => { - setProjectId(undefined) - setLockedUntil(undefined) - } - - // Validates new or newly edited split, then adds it to or edits the splits list - const confirmSplit = async () => { - await form.validateFields() - const roundedLockedUntil = lockedUntil - ? Math.round(lockedUntil.valueOf() / 1000) - : undefined - - const allocator = form.getFieldValue('allocator') - ? utils.getAddress(form.getFieldValue('allocator')) - : undefined - - // if allocator specified, set beneficiary to zero address. Otherwise, set beneficiary to specified address. - const beneficiary = - allocator && allocator !== NULL_ALLOCATOR_ADDRESS - ? constants.AddressZero - : form.getFieldValue('beneficiary') - - const newSplit = { - beneficiary: utils.getAddress(beneficiary), - percent: splitPercentFrom(form.getFieldValue('percent')).toNumber(), - lockedUntil: roundedLockedUntil, - preferClaimed: true, - projectId: projectId, - allocator: allocator, - } as Split - - let adjustedSplits: Split[] = splits - // If an amount and therefore the distribution limit has been changed, - // recalculate all split percents based on newly added split amount - if (newDistributionLimit && !distributionLimitIsInfinite) { - adjustedSplits = adjustedSplitPercents({ - splits, - oldDistributionLimit: distributionLimit ?? '0', - newDistributionLimit, - }) - setDistributionLimit?.(newDistributionLimit) - } - - const newSplits = - mode === 'Edit' - ? adjustedSplits.map(m => - m.beneficiary === editingSplit?.beneficiary && - m.projectId === editingSplit?.projectId - ? { - ...m, - ...newSplit, - } - : m, - ) - : [...adjustedSplits, newSplit] - onSplitsChanged?.(newSplits) - - resetStates() - form.resetFields() - - onClose() - } - - /** - * Set new distribution limit - */ - useEffect(() => { - if (distributionLimitIsInfinite || !amount) return - - const newAmount = parseFloat(stripCommas(amount)) - - const newDistributionLimit = getNewDistributionLimit({ - currentDistributionLimit: distributionLimit ?? '0', - newSplitAmount: newAmount, - editingSplitPercent: mode === 'Add' ? 0 : editingSplit?.percent ?? 0, //percentPerBillion, - }) - - const newPercent = getDistributionPercentFromAmount({ - amount: newAmount, - distributionLimit: isEditPayoutPage - ? parseFloat(distributionLimit ?? '0') - : newDistributionLimit, - }) - - setNewDistributionLimit(newDistributionLimit.toString()) - form.setFieldsValue({ - percent: preciseFormatSplitPercent(newPercent), - }) - }, [ - isEditPayoutPage, - amount, - distributionLimit, - distributionLimitIsInfinite, - editingSplit?.percent, - form, - mode, - ]) - - // Validates new payout receiving address - const validatePayoutAddress = () => { - const beneficiary = form.getFieldValue('beneficiary') - if (!beneficiary) return Promise.reject('Beneficiary required') - if (editingSplit?.beneficiary === beneficiary) { - return Promise.resolve() - } - return validateEthAddress( - beneficiary ?? '', - splits, - mode, - findIndex( - splits, - s => - s.beneficiary === editingSplit?.beneficiary && - s.projectId === editingSplit?.projectId, - ), - // can have a token beneficiary who is also a payout - editingSplitType === 'project', // canBeDuplicate - ) - } - - // Cannot select days before today or today with lockedUntil - const disabledDate = (current: moment.Moment) => - current && current < moment().endOf('day') - - const allocator = useWatch('allocator', form) - - const hasAllocator = allocator && allocator !== NULL_ALLOCATOR_ADDRESS - - return ( - { - resetStates() - form.resetFields() - onClose() - }} - destroyOnClose - > -
{ - if (e.key === 'Enter') confirmSplit() - }} - > - - setEditingSplitType(e.target.value)} - > - - Wallet address - - - Juicebox project - - - - {editingSplitType === 'address' ? ( - - - - ) : ( - - )} - {editingSplitType === 'project' && !hasAllocator ? ( - - - - ) : null} - {/* Only show amount input if project distribution limit is not infinite */} - {!distributionLimitIsInfinite && - (distributionType === 'amount' || distributionType === 'both') ? ( - - ) : null} - {distributionType === 'percent' || distributionType === 'both' ? ( - - ) : null} - - setLockedUntil(lockedUntil)} - /> - - -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/PercentageFormItem.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/PercentageFormItem.tsx deleted file mode 100644 index 507cc49e4b..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/PercentageFormItem.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Form, FormInstance } from 'antd' -import TooltipIcon from 'components/TooltipIcon' -import CurrencySymbol from 'components/currency/CurrencySymbol' -import NumberSlider from 'components/inputs/NumberSlider' -import { CurrencyName } from 'constants/currency' -import { amountFromPercent } from 'utils/v2v3/distributions' -import { AddOrEditSplitFormFields } from './types' -import { percentageValidator } from './utils' - -export function PercentageFormItem({ - form, - distributionLimit, - distributionType, - currencyName, -}: { - form: FormInstance - distributionType: 'amount' | 'percent' | 'both' - distributionLimit?: string - currencyName: CurrencyName -}) { - return ( - -
-
- { - const newAmount = amountFromPercent({ - percent: percentage ?? 0, - amount: distributionLimit ?? '0', - }) - - form.setFieldsValue({ amount: newAmount.toString() }) - form.setFieldsValue({ percent: percentage }) - }} - step={0.01} - defaultValue={0} - sliderValue={form.getFieldValue('percent') ?? 0} - suffix="%" - name="percent" - formItemProps={{ - rules: [{ validator: percentageValidator }], - }} - /> -
- {distributionType === 'both' ? ( - - If you don't raise the sum of your payouts ( - - {distributionLimit}), this address will receive{' '} - {form.getFieldValue('percent')}% of the ETH you raise. - - } - placement={'topLeft'} - iconClassName={'ml-3 mb-4'} - /> - ) : null} -
-
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/V2V3ProjectPayoutFormItem.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/V2V3ProjectPayoutFormItem.tsx deleted file mode 100644 index be992254dd..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/V2V3ProjectPayoutFormItem.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { t } from '@lingui/macro' -import { Form, InputNumber } from 'antd' -import { stringIsDigit } from 'utils/math' -import { AllocatorFormItem } from './AllocatorFormItem' - -export function V2V3ProjectPayoutFormItem({ - value, - onChange, -}: { - value: string | undefined - onChange: (projectId: string | undefined) => void -}) { - const validateProjectId = () => { - if (!stringIsDigit(value ?? '')) { - return Promise.reject(t`Project ID must be a number.`) - } - // TODO: check if projectId exists - return Promise.resolve() - } - - return ( - <> - - - { - onChange(projectId?.toString()) - }} - /> - - - ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/types.ts b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/types.ts deleted file mode 100644 index 34b4d643bd..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as Moment from 'moment' - -export type SplitType = 'project' | 'address' - -export type AddOrEditSplitFormFields = { - projectId: string - beneficiary: string - percent: number - amount: string | undefined - lockedUntil: Moment.Moment | undefined | null - allocator: string | undefined -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/utils.ts b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/utils.ts deleted file mode 100644 index 2766bc1d47..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DistributionSplitModal/utils.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RuleObject } from 'antd/lib/form' -import { StoreValue } from 'antd/lib/form/interface' -import { validatePercentage } from 'components/formItems/formHelpers' -import { preciseFormatSplitPercent } from 'utils/v2v3/math' - -export function percentageValidator(_rule: RuleObject, value: StoreValue) { - return validatePercentage(preciseFormatSplitPercent(value ?? 0)) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DrawerSection.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DrawerSection.tsx deleted file mode 100644 index 3a14dc6871..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/DrawerSection.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export const DrawSection: React.FC> = ({ - children, -}) => { - return ( -
- {children} -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingCycleDrawer.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingCycleDrawer.tsx deleted file mode 100644 index e91403e130..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingCycleDrawer.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Drawer } from 'antd' -import { drawerStyle } from 'constants/styles/drawerStyle' -import { PropsWithChildren } from 'react' - -/** - * Provides the blueprint for Funding Cycle Drawers. - */ -export default function FundingCycleDrawer({ - title, - open, - onClose, - children, -}: PropsWithChildren<{ - title: string - open: boolean - onClose: VoidFunction -}>) { - return ( - -

{title}

- {children} -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingDrawer.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingDrawer.tsx deleted file mode 100644 index a47d41f7b8..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingDrawer.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { t } from '@lingui/macro' -import UnsavedChangesModal from 'components/modals/UnsavedChangesModal' -import FundingCycleDrawer from '../FundingCycleDrawer' -import { useFundingCycleDrawer } from '../hooks/useFundingCycleDrawer' -import { FundingForm } from './FundingForm/FundingForm' - -export function FundingDrawer({ - open, - onClose, - isCreate, -}: { - open: boolean - onClose: VoidFunction - isCreate?: boolean -}) { - const { - handleDrawerCloseClick, - emitDrawerClose, - setFormUpdated, - unsavedChangesModalVisible, - closeModal, - } = useFundingCycleDrawer(onClose) - - return ( - <> - - - - { - closeModal() - emitDrawerClose() - }} - onCancel={closeModal} - /> - - ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/DistributionSplitsSection.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/DistributionSplitsSection.tsx deleted file mode 100644 index a4e88aaf67..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/DistributionSplitsSection.tsx +++ /dev/null @@ -1,315 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Form, Radio } from 'antd' -import { FormItemExt } from 'components/formItems/formItemExt' -import { FEES_EXPLANATION } from 'components/strings' -import TooltipIcon from 'components/TooltipIcon' -import { - Allocation, - AllocationSplit, -} from 'components/v2v3/shared/Allocation/Allocation' -import DistributionLimit from 'components/v2v3/shared/DistributionLimit' -import { OwnerPayoutCard } from 'components/v2v3/shared/PayoutCard/OwnerPayoutCard' -import { PayoutCard } from 'components/v2v3/shared/PayoutCard/PayoutCard' -import { CurrencyName } from 'constants/currency' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { BigNumber } from 'ethers' -import { PayoutsSelection } from 'models/payoutsSelection' -import { Split } from 'models/splits' -import { V2V3CurrencyOption } from 'models/v2v3/currencyOption' -import { useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { classNames } from 'utils/classNames' -import { fromWad, parseWad } from 'utils/format/formatNumber' -import { ceilIfCloseToNextInteger } from 'utils/math' -import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' -import { V2V3_CURRENCY_ETH } from 'utils/v2v3/currency' -import { getTotalSplitsPercentage } from 'utils/v2v3/distributions' -import { MAX_DISTRIBUTION_LIMIT, splitPercentFrom } from 'utils/v2v3/math' -import { DistributionSplitModal } from '../../../DistributionSplitModal/DistributionSplitModal' -import { PayoutConfigurationExplainerCollapse } from './PayoutConfigurationExplainerCollapse' -import SpecificLimitModal from './SpecificLimitModal' - -type DistributionType = 'amount' | 'percent' - -export function DistributionSplitsSection({ - distributionLimit, - setDistributionLimit, - currencyName, - onCurrencyChange, - editableSplits, - lockedSplits, - projectOwnerAddress, - onSplitsChanged, - formItemProps, -}: { - distributionLimit: string | undefined - setDistributionLimit: (distributionLimit: string) => void - currencyName: CurrencyName - onCurrencyChange: (currencyName: CurrencyName) => void - editableSplits: Split[] - lockedSplits: Split[] - projectOwnerAddress: string | undefined - onSplitsChanged: (splits: Split[]) => void -} & FormItemExt) { - const { - payoutSplits: contextPayoutSplits, - distributionLimitCurrency: distributionLimitCurrencyBigNumber, - } = useContext(V2V3ProjectContext) - const distributionLimitIsInfinite = - !distributionLimit || parseWad(distributionLimit).eq(MAX_DISTRIBUTION_LIMIT) - - const [addSplitModalVisible, setAddSplitModalVisible] = - useState(false) - - const [distributionType, setDistributionType] = useState( - distributionLimitIsInfinite ? 'percent' : 'amount', - ) - - const [specificLimitModalOpen, setSpecificLimitModalOpen] = - useState(false) - - const allSplits = lockedSplits.concat(editableSplits) - - const availableModes: Set<'amount' | 'percentage'> = useMemo(() => { - switch (distributionType) { - case 'amount': - return new Set(['amount', 'percentage']) - case 'percent': - return new Set(['percentage']) - } - }, [distributionType]) - - const payoutsSelection: PayoutsSelection = useMemo(() => { - // As we dont have control of amounts/percentage out of create, always use - // amounts, and fall back to percentages when amounts is unavailable. - if ( - !distributionLimit || - parseWad(distributionLimit).eq(0) || - parseWad(distributionLimit).eq(MAX_DISTRIBUTION_LIMIT) - ) { - return 'percentages' - } - return 'amounts' - }, [distributionLimit]) - - const distributionLimitCurrency: V2V3CurrencyOption = useMemo(() => { - const currency = distributionLimitCurrencyBigNumber?.toNumber() - if (!currency || currency === 0) return V2V3_CURRENCY_ETH - return currency as V2V3CurrencyOption - }, [distributionLimitCurrencyBigNumber]) - - const onAllocationsChanged = useCallback( - (allocations: AllocationSplit[]) => - onSplitsChanged(allocations.map(allocationToSplit)), - [onSplitsChanged], - ) - - const setTotalAllocationAmount = useCallback( - (amount: BigNumber) => { - setDistributionLimit(fromWad(amount)) - }, - [setDistributionLimit], - ) - - useEffect(() => { - setDistributionType(distributionLimitIsInfinite ? 'percent' : 'amount') - }, [distributionLimitIsInfinite]) - - if (!allSplits) return null - - const totalSplitsPercentage = getTotalSplitsPercentage(allSplits) - const totalSplitsPercentageInvalid = totalSplitsPercentage > 100 - const remainingSplitsPercentage = 100 - getTotalSplitsPercentage(allSplits) // this amount goes to the project owner - let ownerSplit: Split | undefined - if (remainingSplitsPercentage) { - ownerSplit = { - beneficiary: projectOwnerAddress, - percent: splitPercentFrom(remainingSplitsPercentage).toNumber(), - } as Split - } - - const isLockedAllocation = useCallback( - (allocation: AllocationSplit) => { - const now = new Date().valueOf() / 1000 - if (!allocation.lockedUntil || allocation.lockedUntil < now) return false - - // Checks if the given split exists in the projectContext splits. - // If it doesn't, then it means it was just added or edited is which case - // we want to still be able to edit it - const contextMatch = contextPayoutSplits - ?.map(splitToAllocation) - .find(confirmed => confirmed.id === allocation.id) - if (contextMatch && contextMatch.lockedUntil) { - // Check to make sure that the original allocation is actually still locked - return contextMatch.lockedUntil > now - } - return false - }, - [contextPayoutSplits], - ) - - return ( - -
- -

- Choose how you would like to set up payouts. -

- - { - const newType = e.target.value - if (newType === 'percent') { - setDistributionLimit(fromWad(MAX_DISTRIBUTION_LIMIT)) - setDistributionType(newType) - } else if (newType === 'amount') { - if (editableSplits.length) { - setSpecificLimitModalOpen(true) - } else { - setDistributionLimit('0') - setDistributionType(newType) - } - if ( - remainingSplitsPercentage && - remainingSplitsPercentage !== 100 && - ownerSplit - ) { - editableSplits.push(ownerSplit) - } - } - }} - value={distributionType} - > -
- - Amounts -

- - Pay out specific amounts of ETH to addresses and projects - each cycle. Any remaining ETH will stay in the project for - future cycles. - -

-
- - Percentages -

- - Pay out percentages of your project's total ETH balance to - addresses or projects. No ETH will stay in the project, - making token redemption impossible. - -

-
-
-
-
- - -
- {payoutsSelection === 'percentages' || - (ceilIfCloseToNextInteger(totalSplitsPercentage) < 100 && ( - - ))} - - {( - modal, - { allocations, removeAllocation, setSelectedAllocation }, - ) => ( - <> - {allocations - .filter(alloc => !isLockedAllocation(alloc)) - .map(allocation => ( - removeAllocation(allocation.id)} - onClick={() => { - setSelectedAllocation(allocation) - modal.open() - }} - /> - ))} - - {allocations.filter(isLockedAllocation).map(allocation => ( - - ))} - - )} - -
-
- - {totalSplitsPercentageInvalid && ( - - Sum of percentages cannot exceed 100%. - - )} - -

- - Payouts to Ethereum addresses incur a 2.5% JBX membership fee - {' '} - -

- -
- - - Payouts{' '} - - : - - - - - - - -
-
- setAddSplitModalVisible(false)} - /> - setSpecificLimitModalOpen(false)} - setDistributionLimit={setDistributionLimit} - currencyName={currencyName} - onCurrencyChange={onCurrencyChange} - /> -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/PayoutConfigurationExplainerCollapse.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/PayoutConfigurationExplainerCollapse.tsx deleted file mode 100644 index b810c35d55..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/PayoutConfigurationExplainerCollapse.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Trans } from '@lingui/macro' -import { MinimalCollapse } from 'components/MinimalCollapse' - -export function PayoutConfigurationExplainerCollapse({ - className, -}: { - className?: string -}) { - return ( - How do I decide?} - > -

- - Use Percentages if you'd like to pay out all of the - ETH in the project. Since no ETH will be left over, your token holders - won't be able to redeem their tokens for ETH. This can make it harder - to build trust with your community. - -

- -

- - Otherwise, use Amounts. Amounts let you pre-define - the amount of ETH you can pay out from the project each cycle. The - leftover ETH can be used for future cycles, or token redemptions if - you enable them. - -

-
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/SpecificLimitModal.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/SpecificLimitModal.tsx deleted file mode 100644 index 9641cc5688..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/DistributionSplitsSection/SpecificLimitModal.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Form, Modal } from 'antd' -import { useForm } from 'antd/lib/form/Form' -import InputAccessoryButton from 'components/buttons/InputAccessoryButton' -import FormattedNumberInput from 'components/inputs/FormattedNumberInput' -import TooltipLabel from 'components/TooltipLabel' - -import { CurrencyName } from 'constants/currency' -import { stripCommas } from 'utils/format/formatNumber' - -export default function SpecificLimitModal({ - open, - onClose, - setDistributionLimit, - currencyName, - onCurrencyChange, -}: { - open: boolean - onClose: VoidFunction - setDistributionLimit: (distributionLimit: string) => void - currencyName: CurrencyName - onCurrencyChange: (currencyName: CurrencyName) => void -}) { - const [form] = useForm<{ distributionLimit: string }>() - - function setNewSplitsFromLimit() { - form.validateFields() - // Remove all commas from distribution limit - const distributionLimit = stripCommas( - form.getFieldValue('distributionLimit'), - ) - setDistributionLimit(distributionLimit) - onClose() - } - - const toggleCurrency = () => { - const newCurrency = currencyName === 'ETH' ? 'USD' : 'ETH' - onCurrencyChange(newCurrency) - } - - return ( - - } - onOk={setNewSplitsFromLimit} - > -
- { - if (!value.match(/^[\d,]+$/g)) { - return Promise.reject() - } - }, - }, - ]} - extra={ - - If there is enough ETH available, each recipient will receive - their percent of this amount each cycle. Otherwise, they will - receive their percent of whatever is available. - - } - /> - } - > - - } - /> - -
-
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingCycleExplainerCollapse.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingCycleExplainerCollapse.tsx deleted file mode 100644 index e4a4e0c02c..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingCycleExplainerCollapse.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Trans } from '@lingui/macro' -import { MinimalCollapse } from 'components/MinimalCollapse' - -export function FundingCycleExplainerCollapse() { - return ( - What are locked cycles?}> -
-

- - Your project's rules are locked in place for the duration of each - locked cycle. You will also gain access to: - -

-
    -
  1. - - Recurring cycles. Among other things, this allows - you to predictably pay out ETH on weekly basis, or some other - regular cadence. - -
  2. -
  3. - - Issuance reduction rate. This allows you to - predictably reduce your project token's issuance rate (tokens per - ETH) over time without needing to manually edit your cycle. - -
  4. -
-
-
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingForm.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingForm.tsx deleted file mode 100644 index 4cf0859945..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/FundingDrawer/FundingForm/FundingForm.tsx +++ /dev/null @@ -1,379 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Button, Form } from 'antd' -import ExternalLink from 'components/ExternalLink' -import FormItemWarningText from 'components/FormItemWarningText' -import SwitchHeading from 'components/SwitchHeading' -import { ItemNoInput } from 'components/formItems/ItemNoInput' -import { durationOptions } from 'components/inputs/DurationInput' -import { ETH_TOKEN_ADDRESS } from 'constants/v2v3/juiceboxTokens' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { V2V3ProjectContractsContext } from 'contexts/v2v3/ProjectContracts/V2V3ProjectContractsContext' -import isEqual from 'lodash/isEqual' -import { Split } from 'models/splits' -import { DurationUnitsOption } from 'models/time' -import { V2V3CurrencyOption } from 'models/v2v3/currencyOption' -import { useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { useAppDispatch } from 'redux/hooks/useAppDispatch' -import { useAppSelector } from 'redux/hooks/useAppSelector' -import { - DEFAULT_FUNDING_CYCLE_METADATA, - editingV2ProjectActions, -} from 'redux/slices/editingV2Project' -import { fromWad } from 'utils/format/formatNumber' -import { - deriveDurationUnit, - otherUnitToSeconds, - secondsToOtherUnit, -} from 'utils/format/formatTime' -import { emitErrorNotification } from 'utils/notifications' -import { helpPagePath } from 'utils/routes' -import { sanitizeSplit } from 'utils/splits' -import { - V2V3CurrencyName, - V2V3_CURRENCY_ETH, - getV2V3CurrencyOption, -} from 'utils/v2v3/currency' -import { getTotalSplitsPercentage } from 'utils/v2v3/distributions' -import { getDefaultFundAccessConstraint } from 'utils/v2v3/fundingCycle' -import { MAX_DISTRIBUTION_LIMIT } from 'utils/v2v3/math' -import { SerializedV2V3FundAccessConstraint } from 'utils/v2v3/serializers' -import { DistributionSplitsSection } from './DistributionSplitsSection/DistributionSplitsSection' -import DurationInputAndSelect from './DurationInputAndSelect' -import { FundingCycleExplainerCollapse } from './FundingCycleExplainerCollapse' - -type FundingFormFields = { - duration?: string - durationUnit?: { label: string; value: DurationUnitsOption } - durationEnabled?: boolean - totalSplitsPercentage?: number -} - -const DEFAULT_FUNDING_CYCLE_DURATION_DAYS = 14 - -export function FundingForm({ - onFormUpdated, - onFinish, - isCreate, -}: { - onFormUpdated?: (updated: boolean) => void - onFinish: VoidFunction - isCreate?: boolean // Instance of FundingForm in create flow -}) { - const { contracts: projectContracts } = useContext( - V2V3ProjectContractsContext, - ) - const { payoutSplits } = useContext(V2V3ProjectContext) - const { projectOwnerAddress } = useContext(V2V3ProjectContext) - - const [splits, setSplits] = useState([]) - // Must differentiate between splits loaded from redux and - // ones just added to be able to still edit splits you've - // added with a lockedUntil - const [editingSplits, setEditingSplits] = useState([]) - const [distributionLimit, setDistributionLimit] = useState< - string | undefined - >('0') - const [distributionLimitCurrency, setDistributionLimitCurrency] = - useState(V2V3_CURRENCY_ETH) - const [durationEnabled, setDurationEnabled] = useState(false) - const [fundingForm] = Form.useForm() - - const dispatch = useAppDispatch() - - // Load redux state - const { fundAccessConstraints, fundingCycleData, payoutGroupedSplits } = - useAppSelector(state => state.editingV2Project) - const fundAccessConstraint = - getDefaultFundAccessConstraint( - fundAccessConstraints, - ) - - // Form initial values set by default - const initialValues = useMemo( - () => ({ - durationSeconds: fundingCycleData ? fundingCycleData.duration : '0', - distributionLimit: fundAccessConstraint?.distributionLimit ?? '0', - distributionLimitCurrency: parseInt( - fundAccessConstraint?.distributionLimitCurrency ?? - V2V3_CURRENCY_ETH.toString(), - ) as V2V3CurrencyOption, - payoutSplits: payoutGroupedSplits.splits, - }), - [fundingCycleData, fundAccessConstraint, payoutGroupedSplits], - ) - - const { - editableSplits, - lockedSplits, - }: { - editableSplits: Split[] - lockedSplits: Split[] - } = useMemo(() => { - const now = new Date().valueOf() / 1000 - - // Checks if the given split exists in the projectContext splits. - // If it doesn't, then it means it was just added or edited is which case - // we want to still be able to edit it - const confirmedSplitsIncludesSplit = (split: Split) => { - let includes = false - payoutSplits?.forEach(confirmedSplit => { - if (isEqual(confirmedSplit, split)) { - includes = true - } - }) - return includes - } - - const isLockedSplit = (split: Split) => { - return ( - split.lockedUntil && - split.lockedUntil > now && - !isCreate && - confirmedSplitsIncludesSplit(split) - ) - } - - const lockedSplits = splits?.filter(split => isLockedSplit(split)) ?? [] - const editableSplits = splits?.filter(split => !isLockedSplit(split)) ?? [] - return { - lockedSplits, - editableSplits, - } - }, [splits, isCreate, payoutSplits]) - - // Loads redux state into form - const resetProjectForm = useCallback(() => { - const _distributionLimit = fundAccessConstraint?.distributionLimit ?? '0' - const _distributionLimitCurrency = parseInt( - fundAccessConstraint?.distributionLimitCurrency ?? - V2V3_CURRENCY_ETH.toString(), - ) as V2V3CurrencyOption - - const durationSeconds = fundingCycleData - ? parseInt(fundingCycleData.duration) - : 0 - setDurationEnabled(durationSeconds > 0) - - const durationUnit = deriveDurationUnit(durationSeconds) - - fundingForm.setFieldsValue({ - durationUnit: durationOptions().find(v => v.value === durationUnit), - duration: secondsToOtherUnit({ - duration: durationSeconds, - unit: durationUnit, - }).toString(), - }) - - const payoutSplits = payoutGroupedSplits?.splits - - setDistributionLimit(_distributionLimit) - setDistributionLimitCurrency(_distributionLimitCurrency) - setSplits(payoutSplits ?? []) - }, [fundingForm, fundingCycleData, fundAccessConstraint, payoutGroupedSplits]) - - const onFundingFormSave = useCallback( - async (fields: FundingFormFields) => { - if (!projectContracts?.JBETHPaymentTerminal) { - emitErrorNotification('Failed to save edits.') - console.error( - 'Failed to save form, project JBETHPaymentTerminal not found.', - ) - return - } - - const fundAccessConstraint: - | SerializedV2V3FundAccessConstraint - | undefined = { - terminal: projectContracts.JBETHPaymentTerminal.address, - token: ETH_TOKEN_ADDRESS, - distributionLimit: distributionLimit ?? fromWad(MAX_DISTRIBUTION_LIMIT), - distributionLimitCurrency: - distributionLimitCurrency?.toString() ?? V2V3_CURRENCY_ETH, - overflowAllowance: '0', // nothing for the time being. - overflowAllowanceCurrency: '0', - } - - const duration = fields?.duration ? parseInt(fields?.duration) : 0 - const durationUnit = fields?.durationUnit ?? durationOptions()[0] - - const durationInSeconds = otherUnitToSeconds({ - duration: duration, - unit: durationUnit.value, - }).toString() - - dispatch( - editingV2ProjectActions.setFundAccessConstraints( - fundAccessConstraint ? [fundAccessConstraint] : [], - ), - ) - dispatch( - editingV2ProjectActions.setPayoutSplits( - lockedSplits.concat(editingSplits).map(sanitizeSplit), - ), - ) - dispatch(editingV2ProjectActions.setDuration(durationInSeconds ?? '0')) - - // reset redemption rate if distributionLimit is 0 - if (!distributionLimit || distributionLimit === '0') { - dispatch( - editingV2ProjectActions.setRedemptionRate( - DEFAULT_FUNDING_CYCLE_METADATA.redemptionRate, - ), - ) - dispatch( - editingV2ProjectActions.setBallotRedemptionRate( - DEFAULT_FUNDING_CYCLE_METADATA.ballotRedemptionRate, - ), - ) - } - - onFinish?.() - }, - [ - editingSplits, - lockedSplits, - projectContracts, - dispatch, - distributionLimit, - distributionLimitCurrency, - onFinish, - ], - ) - - // Ensures total split percentages do not exceed 100 - const validateTotalSplitsPercentage = () => { - if (fundingForm.getFieldValue('totalSplitsPercentage') > 100) - return Promise.reject() - return Promise.resolve() - } - - const onFormChange = useCallback(() => { - const duration = fundingForm.getFieldValue('duration') as number - const durationUnit = fundingForm.getFieldValue( - 'durationUnit', - ) as DurationUnitsOption - - const durationInSeconds = durationEnabled - ? otherUnitToSeconds({ - duration: duration, - unit: durationUnit, - }).toString() - : '0' - const splits = lockedSplits.concat(editingSplits).map(sanitizeSplit) - const hasFormUpdated = - initialValues.durationSeconds !== durationInSeconds || - initialValues.distributionLimit !== distributionLimit || - initialValues.distributionLimitCurrency !== distributionLimitCurrency || - !isEqual(initialValues.payoutSplits, splits) - onFormUpdated?.(hasFormUpdated) - }, [ - durationEnabled, - editingSplits, - fundingForm, - initialValues, - lockedSplits, - onFormUpdated, - distributionLimitCurrency, - distributionLimit, - ]) - - useEffect(() => setEditingSplits(editableSplits), [editableSplits]) - - // initially fill form with any existing redux state - useEffect(() => { - resetProjectForm() - }, [resetProjectForm]) - - useEffect(() => { - onFormChange() - }, [onFormChange]) - - return ( -
-
- { - setDurationEnabled(checked) - - // if no funding cycle, select a 0-ballot - if (!checked) { - fundingForm.setFieldsValue({ duration: '0' }) - } else { - fundingForm.setFieldsValue({ - duration: DEFAULT_FUNDING_CYCLE_DURATION_DAYS.toString(), - }) - } - }} - > - Locked cycles - - - {!durationEnabled ? ( - - - With unlocked cycles, the project's owner can edit the project's - rules and start a new cycle (Cycle #2) at any time.{' '} - - Learn more. - - - - ) : null} - - {durationEnabled && } - -
- -
-
- -
-

- Payouts -

- - - setDistributionLimitCurrency(getV2V3CurrencyOption(currencyName)) - } - editableSplits={editingSplits} - lockedSplits={lockedSplits} - projectOwnerAddress={projectOwnerAddress} - onSplitsChanged={newSplits => { - setEditingSplits(newSplits) - fundingForm.setFieldsValue({ - totalSplitsPercentage: getTotalSplitsPercentage(newSplits), - }) - }} - /> - -
- - - - -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/AddNftsSection.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/AddNftsSection.tsx deleted file mode 100644 index c4a620ddd6..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/AddNftsSection.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Button, Empty, Form } from 'antd' -import { RewardsList } from 'components/NftRewards/RewardsList/RewardsList' - -import { useCallback, useState } from 'react' -import { emitErrorNotification } from 'utils/notifications' -import { useFundingCycleDrawer } from '../../hooks/useFundingCycleDrawer' -import { NftCollectionDetailsFormItems } from '../shared/NftCollectionDetailsFormItems' -import { useAddNfts } from './hooks/useAddNfts' -import { useSaveNewCollection } from './hooks/useSaveNewCollection' - -export function AddNftsSection({ onClose }: { onClose: VoidFunction }) { - const [submitLoading, setSubmitLoading] = useState(false) - const { setFormUpdated } = useFundingCycleDrawer(onClose) - - const { rewardTiers, marketplaceForm, postPayModalForm, setRewardTiers } = - useAddNfts({ setFormUpdated }) - const saveNewCollection = useSaveNewCollection({ - rewardTiers, - marketplaceForm, - postPayModalForm, - }) - - const onNftFormSaved = useCallback(async () => { - const collectionName = marketplaceForm.getFieldValue('collectionName') - const collectionSymbol = marketplaceForm.getFieldValue('collectionSymbol') - if (!rewardTiers || !collectionName || !collectionSymbol) { - const message = !rewardTiers - ? t`Add an NFT` - : !collectionName - ? t`Add a collection name` - : t`Add a collection symbol` - emitErrorNotification(message) - return - } - - setSubmitLoading(true) - - await saveNewCollection() - - setSubmitLoading(false) - setFormUpdated(false) - - onClose?.() - }, [rewardTiers, saveNewCollection, onClose, setFormUpdated, marketplaceForm]) - - return ( - <> -

- Reward supporters with NFTs when they pay your project. -

- -
- - - {rewardTiers?.length === 0 && ( - - )} -
- {/* Hack - this whole thing should be a form */} -
- - - - - - ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useAddNfts.ts b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useAddNfts.ts deleted file mode 100644 index ad5f646f54..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useAddNfts.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { useForm } from 'antd/lib/form/Form' -import { NftRewardTier } from 'models/nftRewards' -import { Dispatch, SetStateAction, useEffect, useState } from 'react' -import { useAppSelector } from 'redux/hooks/useAppSelector' -import { tiersEqual } from 'utils/nftRewards' -import { - MarketplaceFormFields, - NftPostPayModalFormFields, -} from '../../shared/formFields' - -export function useAddNfts({ - setFormUpdated, -}: { - setFormUpdated?: Dispatch> -} = {}) { - const [rewardTiers, setRewardTiers] = useState() - const [postPayModalForm] = useForm() - const [marketplaceForm] = useForm() - - const { nftRewards } = useAppSelector(state => state.editingV2Project) - - const editRewardTier = ({ - index, - newRewardTier, - }: { - index: number - newRewardTier: NftRewardTier - }) => { - if ( - rewardTiers && - !tiersEqual({ tier1: newRewardTier, tier2: rewardTiers[index] }) - ) { - setFormUpdated?.(true) - } - - const newRewardTiers = rewardTiers?.map((tier, idx) => - idx === index - ? { - ...tier, - ...newRewardTier, - } - : tier, - ) ?? [newRewardTier] - setRewardTiers(newRewardTiers) - } - - // Load the redux state into the state variable - useEffect(() => { - setRewardTiers(nftRewards.rewardTiers) - }, [nftRewards.rewardTiers]) - - return { - rewardTiers, - setRewardTiers, - postPayModalForm, - marketplaceForm, - editRewardTier, - } -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useSaveNewCollection.ts b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useSaveNewCollection.ts deleted file mode 100644 index 2e714eadc8..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/AddNftsSection/hooks/useSaveNewCollection.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { FormInstance } from 'antd' -import { JB721GovernanceType, NftRewardTier } from 'models/nftRewards' -import { useCallback } from 'react' -import { useAppDispatch } from 'redux/hooks/useAppDispatch' -import { useAppSelector } from 'redux/hooks/useAppSelector' -import { editingV2ProjectActions } from 'redux/slices/editingV2Project' -import { withHttps } from 'utils/externalLink' -import { pinNftCollectionMetadata, pinNftRewards } from 'utils/nftRewards' -import { emitErrorNotification } from 'utils/notifications' - -// When project didn't have any NFTs before reconfiguring. -// i.e. reconfiguring with a new NFT collection (calls `721Deployer.reconfigureFundingCyclesOf` -// when executing reconfiguration on settings page) -export function useSaveNewCollection({ - rewardTiers, - marketplaceForm, - postPayModalForm, -}: { - rewardTiers: NftRewardTier[] | undefined - marketplaceForm: FormInstance - postPayModalForm: FormInstance -}) { - const { - projectMetadata: { logoUri, infoUri }, - } = useAppSelector(state => state.editingV2Project) - const dispatch = useAppDispatch() - - const saveNewCollection = useCallback(async () => { - if (!rewardTiers) { - emitErrorNotification('You must add an NFT tier.') - return - } - - const marketplaceFormValues = marketplaceForm.getFieldsValue(true) - const collectionName = marketplaceFormValues.collectionName - const governance = - marketplaceFormValues.onChainGovernance ?? JB721GovernanceType.NONE - const postPayModalContent = postPayModalForm.getFieldValue('content') - - const [rewardTiersCIDs, nftCollectionMetadataUri] = await Promise.all([ - pinNftRewards(rewardTiers), - pinNftCollectionMetadata({ - collectionName: marketplaceFormValues.collectionName, - collectionDescription: marketplaceFormValues.collectionDescription, - collectionLogoUri: logoUri, - collectionInfoUri: infoUri, - }), - ]) - - dispatch(editingV2ProjectActions.setNftRewardsName(collectionName)) - dispatch( - editingV2ProjectActions.setNftRewardsSymbol( - marketplaceFormValues.collectionSymbol, - ), - ) - dispatch( - editingV2ProjectActions.setNftRewardsCollectionDescription( - marketplaceFormValues.collectionDescription, - ), - ) - dispatch(editingV2ProjectActions.setNftRewardTiers(rewardTiers)) - dispatch( - editingV2ProjectActions.setNftRewardsCollectionMetadataUri( - nftCollectionMetadataUri, - ), - ) - dispatch( - editingV2ProjectActions.setNftPostPayModalConfig( - postPayModalContent - ? { - ctaLink: withHttps(postPayModalForm.getFieldValue('ctaLink')), - ctaText: postPayModalForm.getFieldValue('ctaText'), - content: postPayModalContent, - } - : undefined, - ), - ) - dispatch(editingV2ProjectActions.setNftRewardsGovernance(governance)) - // Store cid (link to nfts on IPFS) to be used later in the deploy tx - dispatch(editingV2ProjectActions.setNftRewardsCIDs(rewardTiersCIDs)) - }, [ - dispatch, - infoUri, - logoUri, - marketplaceForm, - postPayModalForm, - rewardTiers, - ]) - - return saveNewCollection -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/DangerZoneSection.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/DangerZoneSection.tsx deleted file mode 100644 index 054b74f3a6..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/DangerZoneSection.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Button } from 'antd' -import { Callout } from 'components/Callout/Callout' -import { constants } from 'ethers' -import { useAppDispatch } from 'redux/hooks/useAppDispatch' -import { useAppSelector } from 'redux/hooks/useAppSelector' -import { editingV2ProjectActions } from 'redux/slices/editingV2Project' - -export function DangerZoneSection({ close }: { close?: VoidFunction }) { - const { fundingCycleMetadata } = useAppSelector( - state => state.editingV2Project, - ) - const dispatch = useAppDispatch() - - function onDetach() { - dispatch(editingV2ProjectActions.setNftRewardTiers([])) - dispatch( - editingV2ProjectActions.setFundingCycleMetadata({ - ...fundingCycleMetadata, - dataSource: constants.AddressZero, - }), - ) - close?.() - } - - return ( -
- - - Detaching NFTs from your cycle has the following effects: -
    -
  • Supporters won't receive NFTs when they pay your project.
  • -
  • Current NFT holders won't be able to redeem their NFTs.
  • -
-

These changes will take effect in your next cycle.

-
-
- -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/NftDrawer.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/NftDrawer.tsx deleted file mode 100644 index 507f9add6e..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/NftDrawer/NftDrawer.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import UnsavedChangesModal from 'components/modals/UnsavedChangesModal' -import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { useHasNftRewards } from 'hooks/JB721Delegate/useHasNftRewards' -import Link from 'next/link' -import { useContext } from 'react' -import { settingsPagePath } from 'utils/routes' -import { DrawSection } from '../DrawerSection' -import FundingCycleDrawer from '../FundingCycleDrawer' -import { useFundingCycleDrawer } from '../hooks/useFundingCycleDrawer' -import { AddNftsSection } from './AddNftsSection/AddNftsSection' -import { DangerZoneSection } from './DangerZoneSection' - -export function NftDrawer({ - open, - onClose, -}: { - open: boolean - onClose: VoidFunction -}) { - const { projectId } = useContext(ProjectMetadataContext) - const { handle } = useContext(V2V3ProjectContext) - const { value: hasExistingNfts } = useHasNftRewards() - - const { - handleDrawerCloseClick, - emitDrawerClose, - unsavedChangesModalVisible, - closeModal, - } = useFundingCycleDrawer(onClose) - - return ( - <> - - {!hasExistingNfts && ( - - - - )} - - {hasExistingNfts && ( - <> - -

- Edit NFTs -

-

- - To edit your current NFT collection, go to the{' '} - - Edit NFTs - {' '} - page. - -

-
- -

- Danger Zone -

- -
- - )} - - { - closeModal() - emitDrawerClose() - }} - onCancel={closeModal} - /> -
- - ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesDrawer.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesDrawer.tsx deleted file mode 100644 index 5ee598d7ed..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesDrawer.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { t } from '@lingui/macro' -import UnsavedChangesModal from 'components/modals/UnsavedChangesModal' -import FundingCycleDrawer from '../FundingCycleDrawer' -import { useFundingCycleDrawer } from '../hooks/useFundingCycleDrawer' -import RulesForm from './RulesForm/RulesForm' - -export function RulesDrawer({ - open, - onClose, -}: { - open: boolean - onClose: VoidFunction -}) { - const { - handleDrawerCloseClick, - emitDrawerClose, - setFormUpdated, - unsavedChangesModalVisible, - closeModal, - } = useFundingCycleDrawer(onClose) - - return ( - <> - - - - { - closeModal() - emitDrawerClose() - }} - onCancel={closeModal} - /> - - ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/RulesForm.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/RulesForm.tsx deleted file mode 100644 index 5e47ba84b1..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/RulesForm.tsx +++ /dev/null @@ -1,318 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Button, Form, Switch } from 'antd' -import FormItemLabel from 'components/FormItemLabel' -import ReconfigurationStrategySelector from 'components/inputs/ReconfigurationStrategy/ReconfigurationStrategySelector' -import { - CONTROLLER_CONFIG_EXPLANATION, - CONTROLLER_MIGRATION_EXPLANATION, - HOLD_FEES_EXPLANATION, - PAUSE_PAYMENTS_EXPLANATION, - PAUSE_TRANSFERS_EXPLANATION, - TERMINAL_CONFIG_EXPLANATION, - TERMINAL_MIGRATION_EXPLANATION, - USE_DATASOURCE_FOR_REDEEM_EXPLANATION, -} from 'components/strings' -import { ballotStrategiesFn } from 'constants/v2v3/ballotStrategies' -import { V2V3ContractsContext } from 'contexts/v2v3/Contracts/V2V3ContractsContext' -import { isAddress } from 'ethers/lib/utils' -import isEqual from 'lodash/isEqual' -import { BallotStrategy } from 'models/ballot' -import { useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { useAppDispatch } from 'redux/hooks/useAppDispatch' -import { useAppSelector } from 'redux/hooks/useAppSelector' -import { editingV2ProjectActions } from 'redux/slices/editingV2Project' -import { getBallotStrategyByAddress } from 'utils/v2v3/ballotStrategies' -import TokenMintingExtra from './TokenMintingExtra' - -export default function RulesForm({ - onFormUpdated, - onFinish, -}: { - onFormUpdated?: (updated: boolean) => void - onFinish: VoidFunction -}) { - const { cv } = useContext(V2V3ContractsContext) - const dispatch = useAppDispatch() - const { fundingCycleMetadata, fundingCycleData } = useAppSelector( - state => state.editingV2Project, - ) - - const ballotStrategies = ballotStrategiesFn({ cv }) - - // Form initial values set by default - const initialValues = useMemo( - () => ({ - pausePay: fundingCycleMetadata.pausePay, - allowMinting: fundingCycleMetadata.allowMinting, - ballotStrategy: getBallotStrategyByAddress( - fundingCycleData.ballot ?? ballotStrategies[0].address, - ), - allowSetTerminals: fundingCycleMetadata.global.allowSetTerminals, - allowSetController: fundingCycleMetadata.global.allowSetController, - allowTerminalMigration: fundingCycleMetadata.allowTerminalMigration, - allowControllerMigration: fundingCycleMetadata.allowControllerMigration, - pauseTransfers: fundingCycleMetadata.global.pauseTransfers, - holdFees: fundingCycleMetadata.holdFees, - useDataSourceForRedeem: fundingCycleMetadata.useDataSourceForRedeem, - }), - [fundingCycleData, fundingCycleMetadata, ballotStrategies], - ) - - const [showMintingWarning, setShowMintingWarning] = useState(false) - const [ballotStrategy, setBallotStrategy] = useState( - initialValues.ballotStrategy, - ) - const [pausePay, setPausePay] = useState(initialValues.pausePay) - const [allowSetTerminals, setAllowSetTerminals] = useState( - initialValues.allowSetTerminals, - ) - const [allowSetController, setAllowSetController] = useState( - initialValues.allowSetController, - ) - const [allowTerminalMigration, setAllowTerminalMigration] = useState( - initialValues.allowTerminalMigration, - ) - const [allowControllerMigration, setAllowControllerMigration] = - useState(initialValues.allowControllerMigration) - const [pauseTransfers, setPauseTransfers] = useState( - initialValues.pauseTransfers, - ) - const [allowMinting, setAllowMinting] = useState( - initialValues.allowMinting, - ) - const [holdFees, setHoldFees] = useState(initialValues.holdFees) - const [useDataSourceForRedeem, setUseDataSourceForRedeem] = useState( - initialValues.useDataSourceForRedeem, - ) - - useEffect(() => { - const hasFormUpdated = - initialValues.allowMinting !== allowMinting || - initialValues.pausePay !== pausePay || - initialValues.allowSetTerminals !== allowSetTerminals || - initialValues.pauseTransfers !== pauseTransfers || - !isEqual(initialValues.ballotStrategy, ballotStrategy) - - onFormUpdated?.(hasFormUpdated) - }, [ - onFormUpdated, - initialValues, - pausePay, - allowMinting, - ballotStrategy, - allowSetTerminals, - pauseTransfers, - ]) - - const onFormSaved = useCallback(() => { - dispatch(editingV2ProjectActions.setPausePay(pausePay)) - dispatch(editingV2ProjectActions.setAllowMinting(allowMinting)) - dispatch(editingV2ProjectActions.setAllowSetTerminals(allowSetTerminals)) - dispatch(editingV2ProjectActions.setAllowSetController(allowSetController)) - dispatch( - editingV2ProjectActions.setAllowTerminalMigration(allowTerminalMigration), - ) - dispatch( - editingV2ProjectActions.setAllowControllerMigration( - allowControllerMigration, - ), - ) - dispatch(editingV2ProjectActions.setPauseTransfers(pauseTransfers ?? false)) - dispatch(editingV2ProjectActions.setBallot(ballotStrategy.address)) - dispatch(editingV2ProjectActions.setHoldFees(holdFees)) - dispatch( - editingV2ProjectActions.setUseDataSourceForRedeem(useDataSourceForRedeem), - ) - onFinish?.() - }, [ - dispatch, - pausePay, - allowMinting, - allowSetTerminals, - allowSetController, - allowTerminalMigration, - allowControllerMigration, - pauseTransfers, - ballotStrategy.address, - holdFees, - useDataSourceForRedeem, - onFinish, - ]) - - const disableSaveButton = - !ballotStrategy || !isAddress(ballotStrategy.address) - - return ( -
-
-
-
-

- Funding rules -

- -
- - Pause payments to this project -
-
- -
- { - setHoldFees(checked) - }} - checked={holdFees} - /> - Hold fees -
-
-
- -
-

- Token rules -

{' '} - - } - > -
- { - setShowMintingWarning(checked) - setAllowMinting(checked) - }} - checked={allowMinting} - /> - Allow owner token minting -
-
- -
- { - setPauseTransfers(checked) - }} - checked={pauseTransfers} - /> - Pause project token transfers -
-
-
- -
-

- Owner permissions -

-

- Configuration rules -

- -
- { - setAllowSetTerminals(checked) - }} - checked={allowSetTerminals} - /> - Allow Payment Terminal configuration -
-
- -
- { - setAllowSetController(checked) - }} - checked={allowSetController} - /> - Allow Controller configuration -
-
- -

- Migration rules -

- -
- { - setAllowTerminalMigration(checked) - }} - checked={allowTerminalMigration} - /> - Allow Payment Terminal migration -
-
- -
- { - setAllowControllerMigration(checked) - }} - checked={allowControllerMigration} - /> - Allow Controller migration -
-
-
- -
-

- NFT rules -

- -
- { - setUseDataSourceForRedeem(checked) - }} - checked={useDataSourceForRedeem} - /> - Use NFTs for redemptions -
-
-
-
- - - Edit deadline - - } - > - - - - - - -
-
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/TokenMintingExtra.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/TokenMintingExtra.tsx deleted file mode 100644 index 97c213d532..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/RulesDrawer/RulesForm/TokenMintingExtra.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import FormItemWarningText from 'components/FormItemWarningText' -import { - OWNER_MINTING_EXPLANATION, - OWNER_MINTING_RISK, -} from 'components/strings' - -export default function TokenMintingExtra({ - showMintingWarning, -}: { - showMintingWarning: boolean -}) { - return ( -
- {OWNER_MINTING_EXPLANATION} - {showMintingWarning && ( - {OWNER_MINTING_RISK} - )} -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenDrawer.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenDrawer.tsx deleted file mode 100644 index e7c2f1dd3f..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenDrawer.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Callout } from 'components/Callout/Callout' -import UnsavedChangesModal from 'components/modals/UnsavedChangesModal' -import { TokenForm } from 'components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/TokenForm' -import FundingCycleDrawer from '../FundingCycleDrawer' -import { useFundingCycleDrawer } from '../hooks/useFundingCycleDrawer' - -export function TokenDrawer({ - open, - onClose, - isCreate, -}: { - open: boolean - onClose: VoidFunction - isCreate?: boolean -}) { - const { - handleDrawerCloseClick, - emitDrawerClose, - setFormUpdated, - unsavedChangesModalVisible, - closeModal, - } = useFundingCycleDrawer(onClose) - - return ( - <> - -

- Design how your tokens should work. -

- - - Project tokens aren't ERC-20 tokens by default. - Once you deploy your project, you can create an ERC-20 for your - holders to claim. This is optional. - - - - -
- { - closeModal() - emitDrawerClose() - }} - onCancel={closeModal} - /> - - ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/MintRateFormItem.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/MintRateFormItem.tsx deleted file mode 100644 index e16583e8bf..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/MintRateFormItem.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Form } from 'antd' -import TooltipLabel from 'components/TooltipLabel' -import FormattedNumberInput from 'components/inputs/FormattedNumberInput' -import { MINT_RATE_EXPLANATION } from 'components/strings' -import { MAX_MINT_RATE } from 'utils/v2v3/math' - -export default function MintRateFormItem({ - value, - onChange, -}: { - value: string | undefined - onChange: (newWeight: string | undefined) => void -}) { - return ( -
- Total issuance rate} - tip={MINT_RATE_EXPLANATION} - /> - } - className="w-full" - required - > - - tokens per ETH paid - - } - value={value} - onChange={onChange} - isInteger - /> - -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/ReservedTokensFormItem.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/ReservedTokensFormItem.tsx deleted file mode 100644 index 58fcb2c9ec..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/ReservedTokensFormItem.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Trans } from '@lingui/macro' -import { useState } from 'react' - -import { FormItems } from 'components/formItems' -import { FormItemExt } from 'components/formItems/formItemExt' -import FormItemWarningText from 'components/FormItemWarningText' -import { V2V3EditReservedTokens } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/V2V3EditReservedTokens' -import { Split } from 'models/splits' -import { DEFAULT_FUNDING_CYCLE_METADATA } from 'redux/slices/editingV2Project' - -export default function ReservedTokensFormItem({ - className, - hideLabel, - initialValue, - reservedTokensSplits, - onReservedTokensSplitsChange, - onChange, - issuanceRate, -}: { - className?: string - initialValue: number | undefined - reservedTokensSplits: Split[] - onReservedTokensSplitsChange: (splits: Split[]) => void - onChange: (val?: number) => void - issuanceRate?: number -} & FormItemExt) { - // Using a state here because relying on the form does not - // pass through updated reservedRate to ProjectTicketMods - const [reservedRate, setReservedRate] = useState( - initialValue, - ) - - const hasReservedRate = !( - reservedRate === undefined || - reservedRate.toString() === DEFAULT_FUNDING_CYCLE_METADATA.reservedRate - ) - - const [reservedRateChecked, setReservedRateChecked] = - useState(hasReservedRate) - - const defaultReservedRateNum = parseInt( - DEFAULT_FUNDING_CYCLE_METADATA.reservedRate, - ) - - return ( -
- { - setReservedRate(val) - onChange(val) - }} - checked={reservedRateChecked} - onToggled={checked => { - setReservedRateChecked(checked) - if (!checked) { - setReservedRate(defaultReservedRateNum) - onChange(defaultReservedRateNum) - } - }} - hideLabel={hideLabel} - issuanceRate={issuanceRate} - /> - - {(hasReservedRate && reservedRateChecked) || - reservedTokensSplits.length ? ( - <> - {!hasReservedRate && ( - - - You have added reserved token recipients, but your reserved rate - is 0%, meaning no tokens will be reserved. Consider adding a - reserved rate greater than 0% or removing the recipients. - - - )} - - - ) : null} -
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/TokenForm.tsx b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/TokenForm.tsx deleted file mode 100644 index 3422578410..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/TokenForm.tsx +++ /dev/null @@ -1,361 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Button, Form } from 'antd' -import { useForm } from 'antd/lib/form/Form' -import { Callout } from 'components/Callout/Callout' -import FormItemWarningText from 'components/FormItemWarningText' -import SwitchHeading from 'components/SwitchHeading' -import { FormItems } from 'components/formItems' -import { ItemNoInput } from 'components/formItems/ItemNoInput' -import NumberSlider from 'components/inputs/NumberSlider' -import { DISCOUNT_RATE_EXPLANATION } from 'components/strings' -import ReservedTokensFormItem from 'components/v2v3/shared/FundingCycleConfigurationDrawers/TokenDrawer/TokenForm/ReservedTokensFormItem' -import { BigNumber } from 'ethers' -import round from 'lodash/round' -import { Split } from 'models/splits' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { useAppDispatch } from 'redux/hooks/useAppDispatch' -import { useAppSelector } from 'redux/hooks/useAppSelector' -import { - DEFAULT_FUNDING_CYCLE_DATA, - DEFAULT_FUNDING_CYCLE_METADATA, - DEFAULT_MINT_RATE, - editingV2ProjectActions, -} from 'redux/slices/editingV2Project' -import { formattedNum } from 'utils/format/formatNumber' -import { sanitizeSplit } from 'utils/splits' -import { getTotalSplitsPercentage } from 'utils/v2v3/distributions' -import { - getDefaultFundAccessConstraint, - hasDistributionLimit, - hasFundingDuration, -} from 'utils/v2v3/fundingCycle' -import { - discountRateFrom, - formatDiscountRate, - formatIssuanceRate, - formatRedemptionRate, - formatReservedRate, - issuanceRateFrom, - redemptionRateFrom, - reservedRateFrom, -} from 'utils/v2v3/math' -import { SerializedV2V3FundAccessConstraint } from 'utils/v2v3/serializers' -import MintRateFormItem from './MintRateFormItem' - -const MAX_DISCOUNT_RATE = 20 // this is an opinionated limit - -function DiscountRateExtra({ - hasDuration, - initialIssuanceRate, - discountRatePercent, - isCreate, -}: { - hasDuration?: boolean - initialIssuanceRate: number - discountRatePercent: number - isCreate?: boolean -}) { - const discountRateDecimal = discountRatePercent * 0.01 - const secondIssuanceRate = round( - initialIssuanceRate - initialIssuanceRate * discountRateDecimal, - 4, - ) - - const thirdIssuanceRate = round( - secondIssuanceRate - secondIssuanceRate * discountRateDecimal, - 4, - ) - - return ( -
- {!hasDuration && ( - - Disabled when your project's cycle has no duration. - - )} -

{DISCOUNT_RATE_EXPLANATION}

- {discountRatePercent > 0 && isCreate && ( - <> - -

- - Your project's tokens will become{' '} - {discountRatePercent}% more expensive each - cycle. - -

-

- - Next cycle, the total issuance rate will be{' '} - - {formattedNum(secondIssuanceRate)} tokens per 1 ETH. - - The cycle after that, the total issuance rate will be{' '} - - {' '} - {formattedNum(thirdIssuanceRate)} tokens per 1 ETH{' '} - - , and so on. - -

-
- - )} -
- ) -} - -export function TokenForm({ - onFormUpdated, - onFinish, - isCreate, -}: { - onFormUpdated?: (updated: boolean) => void - onFinish: VoidFunction - isCreate?: boolean // If the instance of this form is in the create flow (not reconfig) -}) { - const [tokenForm] = useForm<{ totalReservedSplitPercent: number }>() - - const dispatch = useAppDispatch() - const { - fundingCycleMetadata, - fundingCycleData, - reservedTokensGroupedSplits, - fundAccessConstraints, - } = useAppSelector(state => state.editingV2Project) - const fundAccessConstraint = - getDefaultFundAccessConstraint( - fundAccessConstraints, - ) - - const canSetRedemptionRate = hasDistributionLimit(fundAccessConstraint) - const hasDuration = hasFundingDuration(fundingCycleData) - const canSetDiscountRate = hasDuration - - // Form initial values set by default - const initialValues = useMemo( - () => ({ - reservedRate: - fundingCycleMetadata.reservedRate ?? - DEFAULT_FUNDING_CYCLE_METADATA.reservedRate, - discountRate: - (canSetDiscountRate && fundingCycleData?.discountRate) || - DEFAULT_FUNDING_CYCLE_DATA.discountRate, - redemptionRate: - (canSetRedemptionRate && fundingCycleMetadata?.redemptionRate) || - DEFAULT_FUNDING_CYCLE_METADATA.redemptionRate, - weight: fundingCycleData?.weight - ? formatIssuanceRate(fundingCycleData?.weight) - : DEFAULT_MINT_RATE.toString(), - }), - [ - fundingCycleMetadata.reservedRate, - fundingCycleMetadata?.redemptionRate, - canSetDiscountRate, - fundingCycleData?.discountRate, - fundingCycleData?.weight, - canSetRedemptionRate, - ], - ) - - /** - * NOTE: these values will all be in their 'native' units, - * e.g. permyriads, parts-per-billion etc. - * - * We will convert these to percentages to pass as - * props later on. - */ - const [reservedRate, setReservedRate] = useState( - initialValues.reservedRate, - ) - const [discountRate, setDiscountRate] = useState( - initialValues.discountRate, - ) - const [redemptionRate, setRedemptionRate] = useState( - initialValues.redemptionRate, - ) - const [weight, setWeight] = useState(initialValues.weight) - - const [discountRateChecked, setDiscountRateChecked] = useState( - fundingCycleData?.discountRate !== DEFAULT_FUNDING_CYCLE_DATA.discountRate, - ) - - const [reservedTokensSplits, setReservedTokensSplits] = useState( - reservedTokensGroupedSplits?.splits, - ) - - const onTokenFormSaved = useCallback(async () => { - await tokenForm.validateFields() - const newReservedTokensSplits = reservedTokensSplits.map(split => - sanitizeSplit(split), - ) - /** - * NOTE: all values dispatched to Redux should be in their 'native' units, - * e.g. permyriads, parts-per-billion etc. - * and NOT percentages. - */ - dispatch( - editingV2ProjectActions.setWeight( - issuanceRateFrom(weight ?? DEFAULT_MINT_RATE.toString()), - ), - ) - dispatch(editingV2ProjectActions.setDiscountRate(discountRate ?? '0')) - dispatch(editingV2ProjectActions.setReservedRate(reservedRate ?? '0')) - - // When setting redemption rate, also set ballotRedemptionRate - dispatch(editingV2ProjectActions.setRedemptionRate(redemptionRate)) - dispatch(editingV2ProjectActions.setBallotRedemptionRate(redemptionRate)) - - dispatch( - editingV2ProjectActions.setReservedTokensSplits(newReservedTokensSplits), - ) - - onFinish?.() - }, [ - dispatch, - reservedTokensSplits, - onFinish, - discountRate, - weight, - reservedRate, - redemptionRate, - tokenForm, - ]) - - useEffect(() => { - const hasFormUpdated = - initialValues.weight !== weight || - initialValues.reservedRate !== reservedRate || - initialValues.discountRate !== discountRate || - initialValues.redemptionRate !== redemptionRate - onFormUpdated?.(hasFormUpdated) - }) - - const reservedRatePercent = parseFloat( - formatReservedRate(BigNumber.from(reservedRate)), - ) - - const discountRatePercent = parseFloat( - formatDiscountRate(BigNumber.from(discountRate)), - ) - - // Total tokens minted as a result of a 1 ETH contribution - const initialMintingRate = parseFloat(weight) ?? DEFAULT_MINT_RATE - const reservedRateDecimal = reservedRatePercent * 0.01 - // Tokens received by contributor's per ETH - const initialIssuanceRate = - initialMintingRate - reservedRateDecimal * initialMintingRate - - const validateTotalReservedPercent = () => { - if (getTotalSplitsPercentage(reservedTokensSplits) > 100) { - return Promise.reject(`Reserved token recipient percentages exceed 100%.`) - } - return Promise.resolve() - } - - return ( -
-
-
- { - setWeight(newWeight ?? DEFAULT_MINT_RATE.toString()) - }} - /> - { - setReservedRate( - reservedRateFrom( - newReservedRatePercentage?.toString() ?? '0', - ).toString(), - ) - }} - reservedTokensSplits={reservedTokensSplits} - onReservedTokensSplitsChange={setReservedTokensSplits} - issuanceRate={parseInt(weight)} - /> - - - } - label={ - { - setDiscountRateChecked(checked) - if (!checked) - setDiscountRate(DEFAULT_FUNDING_CYCLE_DATA.discountRate) - }} - checked={discountRateChecked} - disabled={!canSetDiscountRate} - > - Issuance reduction rate - {!discountRateChecked && canSetDiscountRate && ( - - {' '} - ({DEFAULT_FUNDING_CYCLE_DATA.discountRate}%) - - )} - - } - > - {canSetDiscountRate && discountRateChecked && ( - - setDiscountRate( - discountRateFrom(value?.toString() ?? '0').toString(), - ) - } - step={0.1} - /> - )} - - - - Redemption rate - - } - value={formatRedemptionRate(BigNumber.from(redemptionRate))} - onChange={newRedemptionRatePercentage => { - setRedemptionRate( - redemptionRateFrom( - newRedemptionRatePercentage?.toString() ?? '0', - ).toString(), - ) - }} - checked={canSetRedemptionRate} - disabled={!canSetRedemptionRate} - /> -
- - - - -
-
- ) -} diff --git a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/hooks/useFundingCycleDrawer.ts b/src/components/v2v3/shared/FundingCycleConfigurationDrawers/hooks/useFundingCycleDrawer.ts deleted file mode 100644 index ec766cc80a..0000000000 --- a/src/components/v2v3/shared/FundingCycleConfigurationDrawers/hooks/useFundingCycleDrawer.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useCallback, useState } from 'react' - -/** - * Provides State for Funding Cycle Drawers. - */ -export function useFundingCycleDrawer(onClose: VoidFunction) { - const [unsavedChangesModalVisible, setUnsavedChangesModalVisible] = - useState(false) - - const [formUpdated, setFormUpdated] = useState(false) - - const emitDrawerClose = onClose - - const openModal = () => setUnsavedChangesModalVisible(true) - const closeModal = () => setUnsavedChangesModalVisible(false) - - const handleDrawerCloseClick = useCallback(() => { - if (!formUpdated) { - return emitDrawerClose() - } - openModal() - }, [formUpdated, emitDrawerClose]) - - return { - emitDrawerClose, - formUpdated, - setFormUpdated, - unsavedChangesModalVisible, - openModal, - closeModal, - handleDrawerCloseClick, - } -} diff --git a/src/components/v2v3/shared/SplitItem/JuiceboxProjectBeneficiary.tsx b/src/components/v2v3/shared/SplitItem/JuiceboxProjectBeneficiary.tsx index 29d3a6294a..9d8ef74a27 100644 --- a/src/components/v2v3/shared/SplitItem/JuiceboxProjectBeneficiary.tsx +++ b/src/components/v2v3/shared/SplitItem/JuiceboxProjectBeneficiary.tsx @@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro' import { Tooltip } from 'antd' import { NULL_ALLOCATOR_ADDRESS } from 'constants/contracts/mainnet/Allocators' import { Split } from 'models/splits' -import { AllocatorBadge } from '../FundingCycleConfigurationDrawers/AllocatorBadge' +import { AllocatorBadge } from '../../../AllocatorBadge' import V2V3ProjectHandleLink from '../V2V3ProjectHandleLink' export function JuiceboxProjectBeneficiary({ diff --git a/src/components/v2v3/shared/V2V3ProjectLink.tsx b/src/components/v2v3/shared/V2V3ProjectLink.tsx index edcac73075..eb7b31048b 100644 --- a/src/components/v2v3/shared/V2V3ProjectLink.tsx +++ b/src/components/v2v3/shared/V2V3ProjectLink.tsx @@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro' import Link from 'next/link' import { twMerge } from 'tailwind-merge' import { v2v3ProjectRoute } from 'utils/routes' -import { AllocatorBadge } from './FundingCycleConfigurationDrawers/AllocatorBadge' +import { AllocatorBadge } from '../../AllocatorBadge' /** * Different from V2V3ProjectHandleLink in that it doesn't use the handle. This can be used outside of V2V3ContractsContext and V2V3ProjectContext. diff --git a/src/hooks/JB721Delegate/transactor/useLaunchFundingCyclesWithNftsTx.ts b/src/hooks/JB721Delegate/transactor/useLaunchFundingCyclesWithNftsTx.ts deleted file mode 100644 index f38e26631d..0000000000 --- a/src/hooks/JB721Delegate/transactor/useLaunchFundingCyclesWithNftsTx.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { t } from '@lingui/macro' -import { CV_V3 } from 'constants/cv' -import { DEFAULT_MEMO } from 'constants/transactionDefaults' -import { TransactionContext } from 'contexts/Transaction/TransactionContext' -import { V2V3ContractsContext } from 'contexts/v2v3/Contracts/V2V3ContractsContext' -import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { useJBPrices } from 'hooks/JBPrices' -import { useDefaultJBController } from 'hooks/defaultContracts/useDefaultJBController' -import { useDefaultJBETHPaymentTerminal } from 'hooks/defaultContracts/useDefaultJBETHPaymentTerminal' -import { TransactorInstance } from 'hooks/useTransactor' -import { LaunchFundingCyclesData } from 'hooks/v2v3/transactor/useLaunchFundingCyclesTx' -import { useLoadV2V3Contract } from 'hooks/v2v3/useLoadV2V3Contract' -import omit from 'lodash/omit' -import { - JBDeployTiered721DelegateData, - JB_DEPLOY_TIERED_721_DELEGATE_DATA_V3_1, -} from 'models/nftRewards' -import { GroupedSplits, SplitGroup } from 'models/splits' -import { JB721DelegateVersion, V2V3ContractName } from 'models/v2v3/contracts' -import { - JBPayDataSourceFundingCycleMetadata, - V2V3FundAccessConstraint, - V2V3FundingCycleData, -} from 'models/v2v3/fundingCycle' -import { useContext } from 'react' -import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project' -import { NftRewardsData } from 'redux/slices/editingV2Project/types' -import { - buildDeployTiered721DelegateData, - buildJB721TierParams, -} from 'utils/nftRewards' -import { V2V3_CURRENCY_ETH } from 'utils/v2v3/currency' -import { - getTerminalsFromFundAccessConstraints, - isValidMustStartAtOrAfter, -} from 'utils/v2v3/fundingCycle' -import { useV2ProjectTitle } from '../../v2v3/useProjectTitle' -import { useJB721DelegateContractAddress } from '../contracts/useJB721DelegateContractAddress' -import { useJBTiered721DelegateProjectDeployer } from '../contracts/useJBTiered721DelegateProjectDeployer' -import { useProjectControllerJB721DelegateVersion } from '../contracts/useProjectJB721DelegateVersion' - -interface LaunchFundingCyclesWithNftsTxArgs { - projectId: number - tiered721DelegateData: NftRewardsData - launchFundingCyclesData: LaunchFundingCyclesData -} - -export interface JB721DelegateLaunchFundingCycleData { - data: V2V3FundingCycleData - metadata: JBPayDataSourceFundingCycleMetadata - memo?: string - fundAccessConstraints: V2V3FundAccessConstraint[] - groupedSplits?: GroupedSplits[] - mustStartAtOrAfter?: string // epoch seconds. anything less than "now" will start immediately. - terminals: string[] -} - -function buildArgs( - version: JB721DelegateVersion, - { - projectId, - deployTiered721DelegateData, - launchFundingCyclesData, - JBControllerAddress, - }: { - projectId: number - JBControllerAddress: string - deployTiered721DelegateData: - | JBDeployTiered721DelegateData - | JB_DEPLOY_TIERED_721_DELEGATE_DATA_V3_1 - launchFundingCyclesData: JB721DelegateLaunchFundingCycleData - }, -) { - const baseArgs = [ - projectId, - deployTiered721DelegateData, //_deployTiered721DelegateData - launchFundingCyclesData, // _launchFundingCyclesData - ] - - if (version === JB721DelegateVersion.JB721DELEGATE_V3) { - return baseArgs - } - return [...baseArgs, JBControllerAddress] // v3.1/3.2 requires us to pass the controller address in -} - -export function useLaunchFundingCyclesWithNftsTx(): TransactorInstance { - const { transactor } = useContext(TransactionContext) - const { contracts } = useContext(V2V3ContractsContext) - const { projectOwnerAddress } = useContext(V2V3ProjectContext) - - const defaultJBController = useDefaultJBController() - const projectTitle = useV2ProjectTitle() - const V3JBDirectory = useLoadV2V3Contract({ - cv: CV_V3, - contractName: V2V3ContractName.JBDirectory, - }) - const V3JBFundingCycleStore = useLoadV2V3Contract({ - cv: CV_V3, - contractName: V2V3ContractName.JBFundingCycleStore, - }) - const V3JBPrices = useJBPrices({ cv: CV_V3 }) - const defaultJBETHPaymentTerminal = useDefaultJBETHPaymentTerminal() - - const JB721DelegateVersion = useProjectControllerJB721DelegateVersion() - const JBTiered721DelegateProjectDeployer = - useJBTiered721DelegateProjectDeployer({ - version: JB721DelegateVersion, - }) - const JBTiered721DelegateStoreAddress = useJB721DelegateContractAddress({ - contractName: 'JBTiered721DelegateStore', - version: JB721DelegateVersion, - }) - - return async ( - { - projectId, - tiered721DelegateData: { - governanceType, - rewardTiers, - CIDs, - collectionMetadata, - flags, - }, - launchFundingCyclesData: { - fundingCycleData, - fundingCycleMetadata, - fundAccessConstraints, - groupedSplits = [], - mustStartAtOrAfter = DEFAULT_MUST_START_AT_OR_AFTER, - }, - }, - txOpts, - ) => { - const collectionName = collectionMetadata.name - - const contractsLoaded = - contracts && - V3JBDirectory && - V3JBFundingCycleStore && - V3JBPrices && - JBTiered721DelegateStoreAddress && - JBTiered721DelegateProjectDeployer && - defaultJBETHPaymentTerminal && - defaultJBController - - if ( - !transactor || - !projectOwnerAddress || - !isValidMustStartAtOrAfter( - mustStartAtOrAfter, - fundingCycleData.duration, - ) || - !collectionName || - !CIDs || - !rewardTiers || - !contractsLoaded - ) { - const missingParam = !transactor - ? 'transactor' - : !projectOwnerAddress - ? 'projectOwnerAddress' - : !contractsLoaded - ? 'contracts' - : null - - txOpts?.onError?.( - new DOMException( - `Transaction failed, missing argument "${ - missingParam ?? '' - }".`, - ), - ) - - return Promise.resolve(false) - } - const tiers = buildJB721TierParams({ - cids: CIDs, - rewardTiers, - version: JB721DelegateVersion, - }) - - const deployTiered721DelegateData = buildDeployTiered721DelegateData( - { - collectionUri: collectionMetadata.uri ?? '', - collectionName, - collectionSymbol: collectionMetadata.symbol ?? '', - currency: V2V3_CURRENCY_ETH, - governanceType, - tiers, - ownerAddress: projectOwnerAddress, - contractAddresses: { - JBDirectoryAddress: V3JBDirectory.address, - JBFundingCycleStoreAddress: V3JBFundingCycleStore.address, - JBPricesAddress: V3JBPrices.address, - JBTiered721DelegateStoreAddress, - }, - flags, - }, - JB721DelegateVersion, - ) - - // NFT launch tx does not accept `useDataSourceForPay` and `dataSource` (see contracts:`JBPayDataSourceFundingCycleMetadata`) - const dataSourceFCMetadata: JBPayDataSourceFundingCycleMetadata = omit( - fundingCycleMetadata, - ['useDataSourceForPay', 'dataSource'], - ) - - const launchFundingCyclesData: JB721DelegateLaunchFundingCycleData = { - data: fundingCycleData, - metadata: dataSourceFCMetadata, - mustStartAtOrAfter, - groupedSplits, - fundAccessConstraints, - terminals: getTerminalsFromFundAccessConstraints( - fundAccessConstraints, - defaultJBETHPaymentTerminal?.address, - ), - memo: DEFAULT_MEMO, - } - - const args = buildArgs(JB721DelegateVersion, { - projectId, - deployTiered721DelegateData, - launchFundingCyclesData, - JBControllerAddress: defaultJBController.address, - }) - - if (!args) { - txOpts?.onError?.( - new DOMException(`Transaction failed, failed to build args`), - ) - - return Promise.resolve(false) - } - - return transactor( - JBTiered721DelegateProjectDeployer, - 'launchFundingCyclesFor', - args, - { - ...txOpts, - title: t`Launch cycles for ${projectTitle}`, - }, - ) - } -} diff --git a/src/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts b/src/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts index ac997eaf17..50b661bc4c 100644 --- a/src/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts +++ b/src/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts @@ -21,8 +21,13 @@ import { JB_721_TIER_PARAMS_V3_2, JB_DEPLOY_TIERED_721_DELEGATE_DATA_V3_1, } from 'models/nftRewards' +import { GroupedSplits, SplitGroup } from 'models/splits' import { V2V3CurrencyOption } from 'models/v2v3/currencyOption' -import { JBPayDataSourceFundingCycleMetadata } from 'models/v2v3/fundingCycle' +import { + JBPayDataSourceFundingCycleMetadata, + V2V3FundAccessConstraint, + V2V3FundingCycleData, +} from 'models/v2v3/fundingCycle' import { useContext } from 'react' import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project' import { buildDeployTiered721DelegateData } from 'utils/nftRewards' @@ -33,7 +38,6 @@ import { import { useV2ProjectTitle } from '../../v2v3/useProjectTitle' import { useJB721DelegateContractAddress } from '../contracts/useJB721DelegateContractAddress' import { useJBTiered721DelegateProjectDeployer } from '../contracts/useJBTiered721DelegateProjectDeployer' -import { JB721DelegateLaunchFundingCycleData } from './useLaunchFundingCyclesWithNftsTx' interface DeployTiered721DelegateData { collectionUri: string @@ -45,6 +49,16 @@ interface DeployTiered721DelegateData { flags: JBTiered721Flags } +interface JB721DelegateLaunchFundingCycleData { + data: V2V3FundingCycleData + metadata: JBPayDataSourceFundingCycleMetadata + memo?: string + fundAccessConstraints: V2V3FundAccessConstraint[] + groupedSplits?: GroupedSplits[] + mustStartAtOrAfter?: string // epoch seconds. anything less than "now" will start immediately. + terminals: string[] +} + interface LaunchProjectWithNftsTxArgs { tiered721DelegateData: DeployTiered721DelegateData projectData: LaunchProjectData diff --git a/src/hooks/v2v3/transactor/useLaunchFundingCyclesTx.ts b/src/hooks/v2v3/transactor/useLaunchFundingCyclesTx.ts deleted file mode 100644 index 8fb0d31784..0000000000 --- a/src/hooks/v2v3/transactor/useLaunchFundingCyclesTx.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { t } from '@lingui/macro' -import { DEFAULT_MEMO } from 'constants/transactionDefaults' -import { TransactionContext } from 'contexts/Transaction/TransactionContext' -import { Contract } from 'ethers' -import { useWallet } from 'hooks/Wallet' -import { useDefaultJBController } from 'hooks/defaultContracts/useDefaultJBController' -import { useDefaultJBETHPaymentTerminal } from 'hooks/defaultContracts/useDefaultJBETHPaymentTerminal' -import { TransactorInstance } from 'hooks/useTransactor' -import { useContext } from 'react' -import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project' -import { - getTerminalsFromFundAccessConstraints, - isValidMustStartAtOrAfter, -} from 'utils/v2v3/fundingCycle' -import { useV2ProjectTitle } from '../useProjectTitle' -import { LaunchProjectData } from './useLaunchProjectTx' - -export type LaunchFundingCyclesData = Omit< - LaunchProjectData, - 'projectMetadataCID' -> - -export function useLaunchFundingCyclesTx({ - JBController, -}: { - JBController?: Contract -} = {}): TransactorInstance< - { - projectId: number - } & LaunchFundingCyclesData -> { - const { transactor } = useContext(TransactionContext) - const defaultJBController = useDefaultJBController() - const defaultJBETHPaymentTerminal = useDefaultJBETHPaymentTerminal() - - const { userAddress } = useWallet() - const projectTitle = useV2ProjectTitle() - - return ( - { - projectId, - fundingCycleData, - fundingCycleMetadata, - fundAccessConstraints, - groupedSplits = [], - mustStartAtOrAfter = DEFAULT_MUST_START_AT_OR_AFTER, - }, - txOpts, - ) => { - if ( - !transactor || - !userAddress || - !defaultJBController || - !defaultJBETHPaymentTerminal || - !isValidMustStartAtOrAfter(mustStartAtOrAfter, fundingCycleData.duration) - ) { - return Promise.resolve(false) - } - - const args = [ - projectId, // _projectId - fundingCycleData, // _data (JBFundingCycleData) - fundingCycleMetadata, // _metadata (JBFundingCycleMetadata) - mustStartAtOrAfter, // _mustStartAtOrAfter - groupedSplits, // _groupedSplits, - fundAccessConstraints, // _fundAccessConstraints, - getTerminalsFromFundAccessConstraints( - fundAccessConstraints, - defaultJBETHPaymentTerminal?.address, - ), // _terminals - DEFAULT_MEMO, - ] - - return transactor( - JBController ?? defaultJBController, - 'launchFundingCyclesFor', - args, - { - ...txOpts, - title: t`Launch cycles for ${projectTitle}`, - }, - ) - } -} diff --git a/src/locales/messages.pot b/src/locales/messages.pot index ba245a896c..1a40b9d768 100644 --- a/src/locales/messages.pot +++ b/src/locales/messages.pot @@ -119,9 +119,6 @@ msgstr "" msgid "Project handle must be unique." msgstr "" -msgid "No distributions configured." -msgstr "" - msgid "Your Name" msgstr "" @@ -155,9 +152,6 @@ msgstr "" msgid "Since their launch in August 2021, SharkDAO has raised over 1000 ETH on Juicebox, acquired six Nouns, and brought together over 900 members. They remain active nearly two years later collaborating with artists for NFT drops and working on governance in the Nouns ecosystem. NounsDAO has passed several SharkDAO proposals including <0>Nouns-based short films and <1>FOMO Nouns. SharkDAO has also <2>helped provide eye exams and glasses to thousands of kids in need, donated 5 ETH to the <3>Coral Restoration Foundation, and started a <4>Juicebox project which raised 60+ ETH for co-founder <5>Del Piero's son's leukemia treatment." msgstr "" -msgid "Payouts to Ethereum addresses incur a 2.5% JBX membership fee" -msgstr "" - msgid "Grant permission" msgstr "" @@ -209,9 +203,6 @@ msgstr "" msgid "History" msgstr "" -msgid "Funding" -msgstr "" - msgid "Cycle details" msgstr "" @@ -251,12 +242,6 @@ msgstr "" msgid "No" msgstr "" -msgid "How do I decide?" -msgstr "" - -msgid "<0>Issuance reduction rate. This allows you to predictably reduce your project token's issuance rate (tokens per ETH) over time without needing to manually edit your cycle." -msgstr "" - msgid "Review & confirm" msgstr "" @@ -317,9 +302,6 @@ msgstr "" msgid "What are held fees?" msgstr "" -msgid "This is the amount of ETH that you can pay out from your project each cycle." -msgstr "" - msgid "See Tx on Safe" msgstr "" @@ -554,9 +536,6 @@ msgstr "" msgid "Unless payments to this project are paused in your cycle's rules, your project can still receive payments directly through the Juicebox protocol contracts." msgstr "" -msgid "Payouts to this project may mint its project tokens. Pick an address to receive those tokens." -msgstr "" - msgid "{0} ago" msgstr "" @@ -650,9 +629,6 @@ msgstr "" msgid "You must grant permission to migrate your V2 tokens." msgstr "" -msgid "Your project's tokens will become <0>{discountRatePercent}% more expensive each cycle." -msgstr "" - msgid "Project owner" msgstr "" @@ -737,9 +713,6 @@ msgstr "" msgid "Setup new NFTs for {projectTitle}" msgstr "" -msgid "<0>Contribution floor: {0} ETH" -msgstr "" - msgid "A collection's name is the full name used on NFT marketplaces (like <0>OpenSea)." msgstr "" @@ -749,9 +722,6 @@ msgstr "" msgid "There is already a payout for this address" msgstr "" -msgid "NFTs (optional)" -msgstr "" - msgid "Allow your legacy project token holders (<0/> or <1/>) to swap their tokens for your <2/> project tokens." msgstr "" @@ -785,9 +755,6 @@ msgstr "" msgid "No past cycles" msgstr "" -msgid "<0>Recurring cycles. Among other things, this allows you to predictably pay out ETH on weekly basis, or some other regular cadence." -msgstr "" - msgid "3-day deadline" msgstr "" @@ -803,9 +770,6 @@ msgstr "" msgid "Set reserved tokens of {projectTitle}" msgstr "" -msgid "Unix timestamp in seconds. Leave blank to start immediately." -msgstr "" - msgid "Project is not owned by a Safe." msgstr "" @@ -911,9 +875,6 @@ msgstr "" msgid "Business" msgstr "" -msgid "Add an NFT" -msgstr "" - msgid "Skipped" msgstr "" @@ -926,9 +887,6 @@ msgstr "" msgid "Automated" msgstr "" -msgid "Design how your tokens should work." -msgstr "" - msgid "Back to settings" msgstr "" @@ -1028,9 +986,6 @@ msgstr "" msgid "Mint {tokensLabel}" msgstr "" -msgid "Funding cycle details" -msgstr "" - msgid "Archive" msgstr "" @@ -1184,9 +1139,6 @@ msgstr "" msgid "Your total legacy tokens" msgstr "" -msgid "Start the V3 cycle as soon as the current V2 cycle ends <0>({0})." -msgstr "" - msgid "Collection Name" msgstr "" @@ -1256,9 +1208,6 @@ msgstr "" msgid "Move your {tokensLabel} from the Juicebox contract to your wallet." msgstr "" -msgid "Otherwise, use <0>Amounts. Amounts let you pre-define the amount of ETH you can pay out from the project each cycle. The leftover ETH can be used for future cycles, or token redemptions if you enable them." -msgstr "" - msgid "Project tokens will be minted to the address that made the payment." msgstr "" @@ -1322,9 +1271,6 @@ msgstr "" msgid "Active" msgstr "" -msgid "Payout amount" -msgstr "" - msgid "{0} {tokenSymbol}/ETH" msgstr "" @@ -1349,9 +1295,6 @@ msgstr "" msgid "100% transparent" msgstr "" -msgid "Edit your project's NFTs." -msgstr "" - msgid "{receivedTokenSymbolText} Token" msgstr "" @@ -1433,9 +1376,6 @@ msgstr "" msgid "Migrate payment terminal" msgstr "" -msgid "Launch cycles for {projectTitle}" -msgstr "" - msgid "Payments" msgstr "" @@ -1508,9 +1448,6 @@ msgstr "" msgid "Days" msgstr "" -msgid "Next cycle, the total issuance rate will be <0>{0} tokens per 1 ETH.The cycle after that, the total issuance rate will be <1>{1} tokens per 1 ETH , and so on." -msgstr "" - msgid "Your NFTs" msgstr "" @@ -1613,9 +1550,6 @@ msgstr "" msgid "({realTokenAllocationPercent}% of total token issuance)." msgstr "" -msgid "Project tokens <0>aren't ERC-20 tokens by default. Once you deploy your project, you can create an ERC-20 for your holders to claim. This is <1>optional." -msgstr "" - msgid "{0} total" msgstr "" @@ -1652,9 +1586,6 @@ msgstr "" msgid "Set aside a percentage of freshly minted NFTs." msgstr "" -msgid "NFTs detached from project." -msgstr "" - msgid "V2" msgstr "" @@ -1778,9 +1709,6 @@ msgstr "" msgid "ETH paid" msgstr "" -msgid "You have added reserved token recipients, but your reserved rate is 0%, meaning no tokens will be reserved. Consider adding a reserved rate greater than 0% or removing the recipients." -msgstr "" - msgid "Deploy a V3 Migration Token for your project." msgstr "" @@ -1808,9 +1736,6 @@ msgstr "" msgid "Holders of this V1 project's token will be able to migrate their tokens to the V3 token you're deploying." msgstr "" -msgid "Sync start time with current V2 funding cycle." -msgstr "" - msgid "Send Message" msgstr "" @@ -1934,9 +1859,6 @@ msgstr "" msgid "Optionally, you can create an ERC-20 token once your project has been deployed. Until then, the Juicebox protocol will track token balances internally, allowing your supporters to receive tokens and redeem them for ETH." msgstr "" -msgid "Set this to the sum of your payouts" -msgstr "" - msgid "{rewardCount, plural, one {reward} other {rewards}}" msgstr "" @@ -1949,15 +1871,9 @@ msgstr "" msgid "Unlimited" msgstr "" -msgid "<0>Website: <1>{0}" -msgstr "" - msgid "Reserved rate" msgstr "" -msgid "With unlocked cycles, the project's owner can edit the project's rules and start a new cycle (Cycle #2) at any time. <0>Learn more." -msgstr "" - msgid "{0} reserved" msgstr "" @@ -2039,9 +1955,6 @@ msgstr "" msgid "Failed to launch Snapshot. Try again." msgstr "" -msgid "Choose how you would like to set up payouts." -msgstr "" - msgid "Upload" msgstr "" @@ -2093,9 +2006,6 @@ msgstr "" msgid "Project name" msgstr "" -msgid "Project ID must be a number." -msgstr "" - msgid "Payments disabled" msgstr "" @@ -2276,9 +2186,6 @@ msgstr "" msgid "Juicebox native" msgstr "" -msgid "To edit your current NFT collection, go to the <0>Edit NFTs page." -msgstr "" - msgid "File must be in a format of {0}" msgstr "" @@ -2372,9 +2279,6 @@ msgstr "" msgid "{days, plural, one {Edits to an upcoming cycle must be submitted at least # day before that cycle starts.} other {Edits to an upcoming cycle must be submitted at least # days before that cycle starts.}}" msgstr "" -msgid "Save NFTs" -msgstr "" - msgid "Transfer ETH from your wallet to this project without minting tokens." msgstr "" @@ -2486,9 +2390,6 @@ msgstr "" msgid "Standard" msgstr "" -msgid "Launch V3 cycle" -msgstr "" - msgid "Required" msgstr "" @@ -2546,9 +2447,6 @@ msgstr "" msgid "To do so, you need to give your legacy token holders the ability to exchange their tokens for V3 tokens. Select <0>Set up token migration below to get started." msgstr "" -msgid "Use <0>Percentages if you'd like to pay out all of the ETH in the project. Since no ETH will be left over, your token holders won't be able to redeem their tokens for ETH. This can make it harder to build trust with your community." -msgstr "" - msgid "Settings" msgstr "" @@ -2597,9 +2495,6 @@ msgstr "" msgid "to close" msgstr "" -msgid "Pay out percentages of your project's total ETH balance to addresses or projects. No ETH will stay in the project, making token redemption impossible." -msgstr "" - msgid "Immediately" msgstr "" @@ -2693,18 +2588,12 @@ msgstr "" msgid "Subscribe to updates" msgstr "" -msgid "Configure your project's token." -msgstr "" - msgid "Games" msgstr "" msgid "While enabled, the project owner can change the project's <0>controller at any time." msgstr "" -msgid "Add a collection name" -msgstr "" - msgid "Unstaked balance" msgstr "" @@ -2747,9 +2636,6 @@ msgstr "" msgid "General" msgstr "" -msgid "What are locked cycles?" -msgstr "" - msgid "Save deadline" msgstr "" @@ -2807,9 +2693,6 @@ msgstr "" msgid "Your project's current cycle has no duration. Edits you make below will take effect immediately." msgstr "" -msgid "If you don't raise the sum of your payouts (<0/>{distributionLimit}), this address will receive {0}% of all the ETH you raise." -msgstr "" - msgid "When checked, payments made through this address will mint the project's ERC-20 tokens. Payments will incur slightly higher gas fees. When unchecked, the Juicebox protocol will internally track the beneficiary's tokens, and they can claim their ERC-20 tokens at any time." msgstr "" @@ -2825,18 +2708,12 @@ msgstr "" msgid "No NFTs" msgstr "" -msgid "NFT rules" -msgstr "" - msgid "See the NFTs and rewards offered by this project." msgstr "" msgid "Images will be cropped to a 1:1 square in thumbnail previews on the Juicebox app." msgstr "" -msgid "Pause project token transfers" -msgstr "" - msgid "Recipients will recieve payouts in ETH" msgstr "" @@ -2906,9 +2783,6 @@ msgstr "" msgid "Your edits will take effect in <0>cycle #{0}. The current cycle (#{currentFCNumber}) won't be altered." msgstr "" -msgid "Owner permissions" -msgstr "" - msgid "Track the total voting power of each address over time. <0>Learn more." msgstr "" @@ -2945,9 +2819,6 @@ msgstr "" msgid "Give permissions to {0} on project #{projectId}" msgstr "" -msgid "Danger Zone" -msgstr "" - msgid "<0>Juicebox has had <1>multiple security audits, and has handled tens of thousands of ETH through its protocol.<2>However, Juicebox is still experimental software. Although the Juicebox contract team have done their part to shape the smart contracts for public use and have tested the code thoroughly, the risk of exploits is never 0%.<3>Due to their public nature, any exploits to the contracts may have irreversible consequences, including loss of ETH. Please use Juicebox with caution.<4><5>Learn more about the risks." msgstr "" @@ -2996,9 +2867,6 @@ msgstr "" msgid "Edit NFT" msgstr "" -msgid "<0>Description: <1/>" -msgstr "" - msgid "Create a project payer address" msgstr "" @@ -3026,9 +2894,6 @@ msgstr "" msgid "What does Juicebox actually do?" msgstr "" -msgid "Save token rules" -msgstr "" - msgid "Archiving your project has the following effects:" msgstr "" @@ -3149,9 +3014,6 @@ msgstr "" msgid "Other Rules" msgstr "" -msgid "Disabled when your project's cycle has no duration." -msgstr "" - msgid "I Agree" msgstr "" @@ -3236,9 +3098,6 @@ msgstr "" msgid "Cancel" msgstr "" -msgid "Detaching NFTs from your cycle has the following effects:<0><1>Supporters won't receive NFTs when they pay your project.<2>Current NFT holders won't be able to redeem their NFTs.<3>These changes will take effect in your next cycle." -msgstr "" - msgid "When enabled, {tokenSymbol} ERC-20 tokens are issued. When disabled, unclaimed {tokenSymbol} tokens will be issued, which the receiver can claim later as ERC-20." msgstr "" @@ -3329,18 +3188,12 @@ msgstr "" msgid "Deploy NFT collection" msgstr "" -msgid "Configure how your project will collect and spend ETH." -msgstr "" - msgid "You must <0>Edit your Cycle to change your total payout amount." msgstr "" msgid "Pay disclosure" msgstr "" -msgid "If there is enough ETH available, each recipient will receive their percent of this amount each cycle. Otherwise, they will receive their percent of whatever is available." -msgstr "" - msgid "Insufficient token balance" msgstr "" @@ -3449,9 +3302,6 @@ msgstr "" msgid "Redeem NFTs for ETH" msgstr "" -msgid "Launch V3 funding cycle" -msgstr "" - msgid "Juicebox is built for the people, by the people." msgstr "" @@ -3539,15 +3389,9 @@ msgstr "" msgid "Next" msgstr "" -msgid "Payouts <0/>:" -msgstr "" - msgid "Unlocked" msgstr "" -msgid "{0}%" -msgstr "" - msgid "Amount" msgstr "" @@ -3644,9 +3488,6 @@ msgstr "" msgid "The project's owner can edit the project's rules and start new cycles at any time." msgstr "" -msgid "Edit your cycle's rules." -msgstr "" - msgid "Reserve 1 NFT of every" msgstr "" @@ -3671,9 +3512,6 @@ msgstr "" msgid "A contract which defines custom behavior which can be used when somebody pays this project or redeems from it. <0>Learn more" msgstr "" -msgid "If you don't raise the sum of your payouts (<0/>{distributionLimit}), this address will receive {0}% of the ETH you raise." -msgstr "" - msgid "Burn {0} {tokensTextShort}" msgstr "" @@ -3761,9 +3599,6 @@ msgstr "" msgid "Social" msgstr "" -msgid "Detach NFTs from cycle" -msgstr "" - msgid "Your payment may purchase tokens from a secondary market instead of minting new tokens. You might receive more tokens depending on the swap price." msgstr "" @@ -3857,9 +3692,6 @@ msgstr "" msgid "We're always looking for talented people to join the DAO and help build Juicebox. Hit the button below to find out more." msgstr "" -msgid "Payouts to other Juicebox projects won't incur fees." -msgstr "" - msgid "Sold out" msgstr "" @@ -3899,12 +3731,6 @@ msgstr "" msgid "With an auction estimate of $10-15 million at Sotheby's, ConstitutionDAO wanted to raise as much as possible and decided to set Payouts to Unlimited. In order to build trust with potential supporters, the campaign set up a 9/13 multisig with core team members and well-known figures in web3. Reserved Rate was also set to 0 to guarantee that everyone had equal access to PEOPLE tokens which would govern the document if they won the auction." msgstr "" -msgid "Your project's rules are locked in place for the duration of each locked cycle. You will also gain access to:" -msgstr "" - -msgid "Set up your payouts" -msgstr "" - msgid "Connected wallet not authorized" msgstr "" @@ -4052,9 +3878,6 @@ msgstr "" msgid "While enabled, this project will use the custom behavior defined in the contract above when somebody pays this project. Exercise caution." msgstr "" -msgid "<0>Max. supply: <1>{0}" -msgstr "" - msgid "No on-chain governance" msgstr "" @@ -4091,9 +3914,6 @@ msgstr "" msgid "Payment amount can't exceed your wallet balance." msgstr "" -msgid "You're about to add NFTs to your cycle. You'll need to <0>grant NFT permissions before launching cycles." -msgstr "" - msgid "Last paid" msgstr "" @@ -4247,9 +4067,6 @@ msgstr "" msgid "Edited payout" msgstr "" -msgid "Pay out specific amounts of ETH to addresses and projects each cycle. Any remaining ETH will stay in the project for future cycles." -msgstr "" - msgid "Send ETH payments to this project." msgstr "" @@ -4328,9 +4145,6 @@ msgstr "" msgid "USD value of ETH transferred" msgstr "" -msgid "Funding distribution" -msgstr "" - msgid "Cycle #1 starts when you create your project. With locked cycles, if you edit your project's rules during Cycle #1, those edits will be <0>queued for the next cycle." msgstr "" @@ -4340,9 +4154,6 @@ msgstr "" msgid "Unsaved changes" msgstr "" -msgid "Reward supporters with NFTs when they pay your project." -msgstr "" - msgid "Making it fun & exciting to discover, launch and manage projects." msgstr "" @@ -4445,9 +4256,6 @@ msgstr "" msgid "Juicebox Project ID" msgstr "" -msgid "Review and deploy" -msgstr "" - msgid "Events" msgstr "" @@ -4463,9 +4271,6 @@ msgstr "" msgid "Amounts" msgstr "" -msgid "Save funding configuration" -msgstr "" - msgid "This will delete the payout for <0/>." msgstr "" @@ -4619,9 +4424,6 @@ msgstr "" msgid "Pay project" msgstr "" -msgid "No reserved tokens configured." -msgstr "" - msgid "Sent reserved tokens (v1)" msgstr "" @@ -4679,9 +4481,6 @@ msgstr "" msgid "REMAINING SUPPLY: {0}/{1}" msgstr "" -msgid "The total amount of payouts each cycle." -msgstr "" - msgid "A reserved rate" msgstr "" @@ -4718,9 +4517,6 @@ msgstr "" msgid "Reconfiguration deadline" msgstr "" -msgid "Funding rules" -msgstr "" - msgid "Start time" msgstr "" @@ -4784,9 +4580,6 @@ msgstr "" msgid "Set custom resolver" msgstr "" -msgid "Project token recipient" -msgstr "" - msgid "Invalid type - contact Juicebox Support" msgstr "" @@ -4913,9 +4706,6 @@ msgstr "" msgid "Current owner" msgstr "" -msgid "Add a collection symbol" -msgstr "" - msgid "Rankings are based on the amount of ETH a project has been paid and the number of payments to a project over the past {TRENDING_WINDOW_DAYS} days." msgstr ""