diff --git a/.example.env b/.example.env index 621a452eb3..ca2d6f22bf 100644 --- a/.example.env +++ b/.example.env @@ -17,7 +17,7 @@ NEXT_PUBLIC_SUBGRAPH_URL= SUBGRAPH_URL= # wallet connect -NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID +NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID= ##### Variables for complete functionality ##### diff --git a/src/packages/v2v3/components/shared/DiffedItem.tsx b/src/components/DiffedItem.tsx similarity index 100% rename from src/packages/v2v3/components/shared/DiffedItem.tsx rename to src/components/DiffedItem.tsx diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleDetailsRow.tsx b/src/components/FundingCycleDetailsRow.tsx similarity index 100% rename from src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleDetailsRow.tsx rename to src/components/FundingCycleDetailsRow.tsx diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItem.tsx b/src/components/FundingCycleListItem.tsx similarity index 96% rename from src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItem.tsx rename to src/components/FundingCycleListItem.tsx index 94844e1586..4cb8016a0c 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItem.tsx +++ b/src/components/FundingCycleListItem.tsx @@ -1,7 +1,7 @@ import { Tooltip } from 'antd' import { twMerge } from 'tailwind-merge' import { classNames } from 'utils/classNames' -import { DiffedItem } from '../../../shared/DiffedItem' +import { DiffedItem } from './DiffedItem' // e.g. 'Distribution limit', 'Start', 'End', etc. export function FundingCycleListItem({ diff --git a/src/components/NftRewards/AddNftCollectionForm/AddNftCollectionForm.tsx b/src/components/NftRewards/AddNftCollectionForm/AddNftCollectionForm.tsx index 10133ec924..4fe7c335db 100644 --- a/src/components/NftRewards/AddNftCollectionForm/AddNftCollectionForm.tsx +++ b/src/components/NftRewards/AddNftCollectionForm/AddNftCollectionForm.tsx @@ -1,14 +1,14 @@ import { RightOutlined } from '@ant-design/icons' import { Trans, t } from '@lingui/macro' import { Form, FormInstance } from 'antd' -import { useLockPageRulesWrapper } from 'components/Create/hooks/useLockPageRulesWrapper' import ExternalLink from 'components/ExternalLink' import TooltipLabel from 'components/TooltipLabel' import { JuiceInput } from 'components/inputs/JuiceTextInput' import { NftRewardTier } from 'models/nftRewards' +import { CreateCollapse } from 'packages/v2v3/components/Create/components/CreateCollapse/CreateCollapse' +import { OptionalHeader } from 'packages/v2v3/components/Create/components/OptionalHeader' +import { useLockPageRulesWrapper } from 'packages/v2v3/components/Create/hooks/useLockPageRulesWrapper' import { inputMustExistRule } from 'utils/antdRules' -import { CreateCollapse } from '../../Create/components/CreateCollapse/CreateCollapse' -import { OptionalHeader } from '../../Create/components/OptionalHeader' import { RewardsList } from '../RewardsList/RewardsList' import { NftAdvancedFormItems } from './NftAdvancedFormItems' import { NftPaymentSuccessFormItems } from './NftPaymentSuccessFormItems' diff --git a/src/components/NftRewards/RewardsList/AddEditRewardModal.tsx b/src/components/NftRewards/RewardsList/AddEditRewardModal.tsx index 7eb9b34689..48ac10911f 100644 --- a/src/components/NftRewards/RewardsList/AddEditRewardModal.tsx +++ b/src/components/NftRewards/RewardsList/AddEditRewardModal.tsx @@ -14,6 +14,8 @@ import { VIDEO_FILE_TYPES } from 'constants/fileTypes' import { pinFile } from 'lib/api/ipfs' import random from 'lodash/random' import { NftRewardTier } from 'models/nftRewards' +import { CreateCollapse } from 'packages/v2v3/components/Create/components/CreateCollapse/CreateCollapse' +import { OptionalHeader } from 'packages/v2v3/components/Create/components/OptionalHeader' import { DEFAULT_NFT_MAX_SUPPLY } from 'packages/v2v3/constants/nftRewards' import { V2V3_CURRENCY_USD } from 'packages/v2v3/utils/currency' import { UploadRequestOption } from 'rc-upload/lib/interface' @@ -28,8 +30,6 @@ import { } from 'utils/antdRules' import { withHttps } from 'utils/externalLink' import { ipfsGatewayUrl } from 'utils/ipfs' -import { CreateCollapse } from '../../Create/components/CreateCollapse/CreateCollapse' -import { OptionalHeader } from '../../Create/components/OptionalHeader' interface AddEditRewardModalFormProps { fileUrl: string diff --git a/src/components/VolumeChart/hooks/useProjectTimeline.ts b/src/components/VolumeChart/hooks/useProjectTimeline.ts index 55520a1c32..cc8fea805e 100644 --- a/src/components/VolumeChart/hooks/useProjectTimeline.ts +++ b/src/components/VolumeChart/hooks/useProjectTimeline.ts @@ -1,8 +1,12 @@ import { useQuery } from '@tanstack/react-query' -import { PV_V4 } from 'constants/pv' +import { PV_V2, PV_V4 } from 'constants/pv' import { readProvider } from 'constants/readProvider' import EthDater from 'ethereum-block-by-date' -import { ProjectTlQuery, useProjectTlQuery } from 'generated/graphql' +import { + ProjectTlQuery, + useProjectsQuery, + useProjectTlQuery, +} from 'generated/graphql' import { client } from 'lib/apollo/client' import { PV } from 'models/pv' import { ProjectTlDocument } from 'packages/v4/graphql/client/graphql' @@ -11,6 +15,8 @@ import { useMemo } from 'react' import { wadToFloat } from 'utils/format/formatNumber' import { getSubgraphIdForProject } from 'utils/graph' import { daysToMS, minutesToMS } from 'utils/units' +import { RomanStormVariables } from 'constants/romanStorm' + import { ProjectTimelinePoint, ProjectTimelineRange } from '../types' const COUNT = 30 @@ -24,6 +30,25 @@ export function useProjectTimeline({ pv: PV range: ProjectTimelineRange }) { + const exceptionTimestamp = useMemo(() => { + return RomanStormVariables.PROJECT_ID === projectId + ? RomanStormVariables.SNAPSHOT_TIMESTAMP + : null + }, [projectId]) + + const { data: romanStormData } = useProjectsQuery({ + client, + fetchPolicy: 'no-cache', + skip: projectId !== RomanStormVariables.PROJECT_ID, + variables: { + where: { + projectId, + pv: PV_V2, + }, + block: { number: RomanStormVariables.SNAPSHOT_BLOCK }, + }, + }) + const { data: blockData, isLoading: isLoadingBlockNumbers } = useQuery({ queryKey: ['block-numbers', range], queryFn: async () => { @@ -70,22 +95,23 @@ export function useProjectTimeline({ return { blocks, timestamps } }, [blockData]) - const { data: v1v2v3QueryResult, loading: isLoadingQuery } = useProjectTlQuery({ - client, - variables: { - id: blocks ? getSubgraphIdForProject(pv, projectId) : '', - ...blocks, - }, - skip: pv === PV_V4 - }) + const { data: v1v2v3QueryResult, loading: isLoadingQuery } = + useProjectTlQuery({ + client, + variables: { + id: blocks ? getSubgraphIdForProject(pv, projectId) : '', + ...blocks, + }, + skip: pv === PV_V4, + }) const { data: v4QueryResult } = useSubgraphQuery({ - document: ProjectTlDocument, + document: ProjectTlDocument, variables: { id: blocks ? projectId.toString() : '', ...blocks, }, - enabled: pv === PV_V4 + enabled: pv === PV_V4, }) const points = useMemo(() => { @@ -95,16 +121,31 @@ export function useProjectTimeline({ const points: ProjectTimelinePoint[] = [] for (let i = 0; i < COUNT; i++) { - const point = (queryResult as ProjectTlQuery)[`p${i}` as keyof typeof queryResult] + const point = (queryResult as ProjectTlQuery)[ + `p${i}` as keyof typeof queryResult + ] if (!point) continue - - points.push({ - timestamp: timestamps[i], - trendingScore: wadToFloat(point.trendingScore), - balance: wadToFloat(point.currentBalance), - volume: wadToFloat(point.volume), - }) + if (exceptionTimestamp && exceptionTimestamp > timestamps[i]) { + points.push({ + timestamp: timestamps[i], + trendingScore: 0, + balance: 0, + volume: 0, + }) + } else { + const volume = + projectId === RomanStormVariables.PROJECT_ID + ? point.volume.sub(romanStormData?.projects[0].volume || 0) + : point.volume + + points.push({ + timestamp: timestamps[i], + trendingScore: wadToFloat(point.trendingScore), + balance: wadToFloat(point.currentBalance), + volume: wadToFloat(volume), + }) + } } return points diff --git a/src/components/formItems/ProjectTwitter.tsx b/src/components/formItems/ProjectTwitter.tsx index 5230ee6147..f41e0f37fb 100644 --- a/src/components/formItems/ProjectTwitter.tsx +++ b/src/components/formItems/ProjectTwitter.tsx @@ -1,7 +1,7 @@ import { t } from '@lingui/macro' import { Form } from 'antd' -import { TwitterHandleInputWrapper } from 'components/Create/components/pages/ProjectDetails/ProjectDetailsPage' +import { TwitterHandleInputWrapper } from 'packages/v2v3/components/Create/components/pages/ProjectDetails/ProjectDetailsPage' import { FormItemExt } from './formItemExt' export default function ProjectTwitter({ diff --git a/src/components/modals/TransactionModal.tsx b/src/components/modals/TransactionModal.tsx index 2b51768c40..a3625b4f4b 100644 --- a/src/components/modals/TransactionModal.tsx +++ b/src/components/modals/TransactionModal.tsx @@ -4,7 +4,7 @@ import { readNetwork } from 'constants/networks' import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext' import { useWallet } from 'hooks/Wallet' import { TxStatus } from 'models/transaction' -import Image from "next/legacy/image" +import Image from 'next/legacy/image' import { PropsWithChildren, useContext, useMemo } from 'react' import EtherscanLink from '../EtherscanLink' @@ -79,6 +79,7 @@ export default function TransactionModal(props: TransactionModalProps) { props.okText, props.switchNetworkText, ]) + const modalProps = { ...props, ...{ diff --git a/src/constants/romanStorm.ts b/src/constants/romanStorm.ts new file mode 100644 index 0000000000..d067a47dc7 --- /dev/null +++ b/src/constants/romanStorm.ts @@ -0,0 +1,9 @@ +const PROJECT_ID = 618 +const SNAPSHOT_BLOCK = 20229376 +const SNAPSHOT_TIMESTAMP = 1720050599 + +export const RomanStormVariables = { + PROJECT_ID, + SNAPSHOT_BLOCK, + SNAPSHOT_TIMESTAMP, +} diff --git a/src/graphql/projects/projects.graphql b/src/graphql/projects/projects.graphql index 6b00dd7e74..2a7da8fcd3 100644 --- a/src/graphql/projects/projects.graphql +++ b/src/graphql/projects/projects.graphql @@ -4,8 +4,9 @@ query Projects( $skip: Int, $orderBy: Project_orderBy $orderDirection: OrderDirection + $block: Block_height ) { - projects(where: $where, first: $first, skip: $skip) { + projects(where: $where, first: $first, skip: $skip, block: $block) { id projectId metadataUri diff --git a/src/graphql/projects/trendingProjects.graphql b/src/graphql/projects/trendingProjects.graphql index a97771b033..c4f6ca5dfa 100644 --- a/src/graphql/projects/trendingProjects.graphql +++ b/src/graphql/projects/trendingProjects.graphql @@ -4,7 +4,7 @@ query TrendingProjects( $skip: Int $orderBy: Project_orderBy $orderDirection: OrderDirection - $block: Block_Height + $block: Block_height ) { projects( where: $where @@ -12,6 +12,7 @@ query TrendingProjects( skip: $skip orderBy: $orderBy orderDirection: desc + block: $block ) { id projectId diff --git a/src/hooks/Wallet/hooks/useChainUnsupported.ts b/src/hooks/Wallet/hooks/useChainUnsupported.ts index 606ee7cfcd..5d83bb9458 100644 --- a/src/hooks/Wallet/hooks/useChainUnsupported.ts +++ b/src/hooks/Wallet/hooks/useChainUnsupported.ts @@ -1,17 +1,26 @@ import { useSetChain } from '@web3-onboard/react' -import { useMemo } from 'react' - import { readNetwork } from 'constants/networks' +import { useCurrentRouteChainId } from 'packages/v4/hooks/useCurrentRouteChainId' +import { useMemo } from 'react' export function useChainUnsupported() { const [{ connectedChain }] = useSetChain() + + // get v4 chain id + const chainId = useCurrentRouteChainId() + const chainUnsupported = useMemo(() => { if (!connectedChain) { return false } + // account for v4 + if (chainId) { + return Number(connectedChain.id) !== chainId + } + return Number(connectedChain.id) !== readNetwork.chainId - }, [connectedChain]) + }, [connectedChain, chainId]) return chainUnsupported } diff --git a/src/hooks/useProjectEvents.ts b/src/hooks/useProjectEvents.ts index 2a00370fa9..2af206d853 100644 --- a/src/hooks/useProjectEvents.ts +++ b/src/hooks/useProjectEvents.ts @@ -1,10 +1,13 @@ +import { useMemo } from 'react' + import { OrderDirection, ProjectEvent_OrderBy, - useProjectEventsQuery + useProjectEventsQuery, } from 'generated/graphql' import { client } from 'lib/apollo/client' import { ProjectEventsQueryArgs } from 'models/projectEvents' +import { RomanStormVariables } from 'constants/romanStorm' export function useProjectEvents({ filter, @@ -14,6 +17,13 @@ export function useProjectEvents({ first, skip, }: ProjectEventsQueryArgs) { + const projectSnapshotTimestamp = useMemo(() => { + if (projectId === RomanStormVariables.PROJECT_ID) { + return RomanStormVariables.SNAPSHOT_TIMESTAMP + } + return null + }, [projectId]) + return useProjectEventsQuery({ client, variables: { @@ -27,6 +37,9 @@ export function useProjectEvents({ where: { ...(pv ? { pv } : {}), ...(projectId ? { projectId } : {}), + ...(projectSnapshotTimestamp + ? { timestamp_gt: projectSnapshotTimestamp } + : {}), ...(from ? { // subgraph needs addresses to be lowercased diff --git a/src/locales/messages.pot b/src/locales/messages.pot index 482c1f426e..1dfce92901 100644 --- a/src/locales/messages.pot +++ b/src/locales/messages.pot @@ -1604,6 +1604,9 @@ msgstr "" msgid "<0>Edits to this project must be made before this deadline. This gives token holders time to verify the edits before they take effect.<1>For example: with a 1-day edit deadline, edits must be made at least 1 day before a cycle starts." msgstr "" +msgid "Reserved {tokenTextPlural}: <0>{pendingReservedTokensFormatted} {tokenTextPlural}" +msgstr "" + msgid "Send Message" msgstr "" @@ -3056,6 +3059,9 @@ msgstr "" msgid "Set a duration for locked cycles." msgstr "" +msgid "Enable project token transfers" +msgstr "" + msgid "Deadline" msgstr "" @@ -3254,6 +3260,9 @@ msgstr "" msgid "Notifications" msgstr "" +msgid "Enable decay rate" +msgstr "" + msgid "Logo" msgstr "" @@ -3602,6 +3611,9 @@ msgstr "" msgid "Owner tools" msgstr "" +msgid "{value} {tokenSymbol}/ETH" +msgstr "" + msgid "<0>Juicebox is a programmable crypto fundraising platform for web3. It helps people fund, operate, and scale their projects transparently using Ethereum, which is a type of programmable cryptocurrency.<1>Juicebox is funded and owned by its community." msgstr "" diff --git a/src/components/Create/Create.tsx b/src/packages/v2v3/components/Create/Create.tsx similarity index 100% rename from src/components/Create/Create.tsx rename to src/packages/v2v3/components/Create/Create.tsx diff --git a/src/components/Create/components/CreateBadge/DefaultBadge.tsx b/src/packages/v2v3/components/Create/components/CreateBadge/DefaultBadge.tsx similarity index 100% rename from src/components/Create/components/CreateBadge/DefaultBadge.tsx rename to src/packages/v2v3/components/Create/components/CreateBadge/DefaultBadge.tsx diff --git a/src/components/Create/components/CreateBadge/OptionalBadge.tsx b/src/packages/v2v3/components/Create/components/CreateBadge/OptionalBadge.tsx similarity index 100% rename from src/components/Create/components/CreateBadge/OptionalBadge.tsx rename to src/packages/v2v3/components/Create/components/CreateBadge/OptionalBadge.tsx diff --git a/src/components/Create/components/CreateBadge/RecommendedBadge.tsx b/src/packages/v2v3/components/Create/components/CreateBadge/RecommendedBadge.tsx similarity index 100% rename from src/components/Create/components/CreateBadge/RecommendedBadge.tsx rename to src/packages/v2v3/components/Create/components/CreateBadge/RecommendedBadge.tsx diff --git a/src/components/Create/components/CreateBadge/SkippedBadge.tsx b/src/packages/v2v3/components/Create/components/CreateBadge/SkippedBadge.tsx similarity index 100% rename from src/components/Create/components/CreateBadge/SkippedBadge.tsx rename to src/packages/v2v3/components/Create/components/CreateBadge/SkippedBadge.tsx diff --git a/src/components/Create/components/CreateBadge/index.ts b/src/packages/v2v3/components/Create/components/CreateBadge/index.ts similarity index 100% rename from src/components/Create/components/CreateBadge/index.ts rename to src/packages/v2v3/components/Create/components/CreateBadge/index.ts diff --git a/src/components/Create/components/CreateCollapse/CreateCollapse.tsx b/src/packages/v2v3/components/Create/components/CreateCollapse/CreateCollapse.tsx similarity index 100% rename from src/components/Create/components/CreateCollapse/CreateCollapse.tsx rename to src/packages/v2v3/components/Create/components/CreateCollapse/CreateCollapse.tsx diff --git a/src/components/Create/components/CreateCollapse/CreateCollapsePanel.tsx b/src/packages/v2v3/components/Create/components/CreateCollapse/CreateCollapsePanel.tsx similarity index 100% rename from src/components/Create/components/CreateCollapse/CreateCollapsePanel.tsx rename to src/packages/v2v3/components/Create/components/CreateCollapse/CreateCollapsePanel.tsx diff --git a/src/components/Create/components/Icons/Infinity.tsx b/src/packages/v2v3/components/Create/components/Icons/Infinity.tsx similarity index 100% rename from src/components/Create/components/Icons/Infinity.tsx rename to src/packages/v2v3/components/Create/components/Icons/Infinity.tsx diff --git a/src/components/Create/components/Icons/ManualSettingsIcon.tsx b/src/packages/v2v3/components/Create/components/Icons/ManualSettingsIcon.tsx similarity index 100% rename from src/components/Create/components/Icons/ManualSettingsIcon.tsx rename to src/packages/v2v3/components/Create/components/Icons/ManualSettingsIcon.tsx diff --git a/src/components/Create/components/Icons/TargetIcon.tsx b/src/packages/v2v3/components/Create/components/Icons/TargetIcon.tsx similarity index 100% rename from src/components/Create/components/Icons/TargetIcon.tsx rename to src/packages/v2v3/components/Create/components/Icons/TargetIcon.tsx diff --git a/src/components/Create/components/Icons/TokensIcon.tsx b/src/packages/v2v3/components/Create/components/Icons/TokensIcon.tsx similarity index 100% rename from src/components/Create/components/Icons/TokensIcon.tsx rename to src/packages/v2v3/components/Create/components/Icons/TokensIcon.tsx diff --git a/src/components/Create/components/Icons/index.ts b/src/packages/v2v3/components/Create/components/Icons/index.ts similarity index 100% rename from src/components/Create/components/Icons/index.ts rename to src/packages/v2v3/components/Create/components/Icons/index.ts diff --git a/src/components/Create/components/OptionalHeader.tsx b/src/packages/v2v3/components/Create/components/OptionalHeader.tsx similarity index 100% rename from src/components/Create/components/OptionalHeader.tsx rename to src/packages/v2v3/components/Create/components/OptionalHeader.tsx diff --git a/src/components/Create/components/Selection/Selection.tsx b/src/packages/v2v3/components/Create/components/Selection/Selection.tsx similarity index 100% rename from src/components/Create/components/Selection/Selection.tsx rename to src/packages/v2v3/components/Create/components/Selection/Selection.tsx diff --git a/src/components/Create/components/Selection/SelectionCard.tsx b/src/packages/v2v3/components/Create/components/Selection/SelectionCard.tsx similarity index 100% rename from src/components/Create/components/Selection/SelectionCard.tsx rename to src/packages/v2v3/components/Create/components/Selection/SelectionCard.tsx diff --git a/src/components/Create/components/Selection/components/CheckedCircle.tsx b/src/packages/v2v3/components/Create/components/Selection/components/CheckedCircle.tsx similarity index 100% rename from src/components/Create/components/Selection/components/CheckedCircle.tsx rename to src/packages/v2v3/components/Create/components/Selection/components/CheckedCircle.tsx diff --git a/src/components/Create/components/Selection/components/RadialBackgroundIcon.tsx b/src/packages/v2v3/components/Create/components/Selection/components/RadialBackgroundIcon.tsx similarity index 100% rename from src/components/Create/components/Selection/components/RadialBackgroundIcon.tsx rename to src/packages/v2v3/components/Create/components/Selection/components/RadialBackgroundIcon.tsx diff --git a/src/components/Create/components/Wizard/Page.tsx b/src/packages/v2v3/components/Create/components/Wizard/Page.tsx similarity index 100% rename from src/components/Create/components/Wizard/Page.tsx rename to src/packages/v2v3/components/Create/components/Wizard/Page.tsx diff --git a/src/components/Create/components/Wizard/PageButtonControl/PageButtonControl.tsx b/src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/PageButtonControl.tsx similarity index 100% rename from src/components/Create/components/Wizard/PageButtonControl/PageButtonControl.tsx rename to src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/PageButtonControl.tsx diff --git a/src/components/Create/components/Wizard/PageButtonControl/components/BackButton.tsx b/src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/components/BackButton.tsx similarity index 100% rename from src/components/Create/components/Wizard/PageButtonControl/components/BackButton.tsx rename to src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/components/BackButton.tsx diff --git a/src/components/Create/components/Wizard/PageButtonControl/components/DoneButton.tsx b/src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/components/DoneButton.tsx similarity index 100% rename from src/components/Create/components/Wizard/PageButtonControl/components/DoneButton.tsx rename to src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/components/DoneButton.tsx diff --git a/src/components/Create/components/Wizard/PageButtonControl/components/NextButton.tsx b/src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/components/NextButton.tsx similarity index 100% rename from src/components/Create/components/Wizard/PageButtonControl/components/NextButton.tsx rename to src/packages/v2v3/components/Create/components/Wizard/PageButtonControl/components/NextButton.tsx diff --git a/src/components/Create/components/Wizard/Steps/MobileProgressModal.tsx b/src/packages/v2v3/components/Create/components/Wizard/Steps/MobileProgressModal.tsx similarity index 100% rename from src/components/Create/components/Wizard/Steps/MobileProgressModal.tsx rename to src/packages/v2v3/components/Create/components/Wizard/Steps/MobileProgressModal.tsx diff --git a/src/components/Create/components/Wizard/Steps/MobileStep.tsx b/src/packages/v2v3/components/Create/components/Wizard/Steps/MobileStep.tsx similarity index 100% rename from src/components/Create/components/Wizard/Steps/MobileStep.tsx rename to src/packages/v2v3/components/Create/components/Wizard/Steps/MobileStep.tsx diff --git a/src/components/Create/components/Wizard/Steps/Steps.tsx b/src/packages/v2v3/components/Create/components/Wizard/Steps/Steps.tsx similarity index 100% rename from src/components/Create/components/Wizard/Steps/Steps.tsx rename to src/packages/v2v3/components/Create/components/Wizard/Steps/Steps.tsx diff --git a/src/components/Create/components/Wizard/Wizard.tsx b/src/packages/v2v3/components/Create/components/Wizard/Wizard.tsx similarity index 100% rename from src/components/Create/components/Wizard/Wizard.tsx rename to src/packages/v2v3/components/Create/components/Wizard/Wizard.tsx diff --git a/src/components/Create/components/Wizard/contexts/PageContext.tsx b/src/packages/v2v3/components/Create/components/Wizard/contexts/PageContext.tsx similarity index 100% rename from src/components/Create/components/Wizard/contexts/PageContext.tsx rename to src/packages/v2v3/components/Create/components/Wizard/contexts/PageContext.tsx diff --git a/src/components/Create/components/Wizard/contexts/WizardContext.tsx b/src/packages/v2v3/components/Create/components/Wizard/contexts/WizardContext.tsx similarity index 100% rename from src/components/Create/components/Wizard/contexts/WizardContext.tsx rename to src/packages/v2v3/components/Create/components/Wizard/contexts/WizardContext.tsx diff --git a/src/components/Create/components/Wizard/hooks/usePage.ts b/src/packages/v2v3/components/Create/components/Wizard/hooks/usePage.ts similarity index 100% rename from src/components/Create/components/Wizard/hooks/usePage.ts rename to src/packages/v2v3/components/Create/components/Wizard/hooks/usePage.ts diff --git a/src/components/Create/components/Wizard/hooks/useSteps.ts b/src/packages/v2v3/components/Create/components/Wizard/hooks/useSteps.ts similarity index 100% rename from src/components/Create/components/Wizard/hooks/useSteps.ts rename to src/packages/v2v3/components/Create/components/Wizard/hooks/useSteps.ts diff --git a/src/components/Create/components/Wizard/hooks/useWizard.ts b/src/packages/v2v3/components/Create/components/Wizard/hooks/useWizard.ts similarity index 100% rename from src/components/Create/components/Wizard/hooks/useWizard.ts rename to src/packages/v2v3/components/Create/components/Wizard/hooks/useWizard.ts diff --git a/src/components/Create/components/pages/FundingCycles/FundingCyclesPage.tsx b/src/packages/v2v3/components/Create/components/pages/FundingCycles/FundingCyclesPage.tsx similarity index 98% rename from src/components/Create/components/pages/FundingCycles/FundingCyclesPage.tsx rename to src/packages/v2v3/components/Create/components/pages/FundingCycles/FundingCyclesPage.tsx index f1ff42e9f8..7caef86242 100644 --- a/src/components/Create/components/pages/FundingCycles/FundingCyclesPage.tsx +++ b/src/packages/v2v3/components/Create/components/pages/FundingCycles/FundingCyclesPage.tsx @@ -7,13 +7,13 @@ import { Trans, t } from '@lingui/macro' import { Form, Tooltip } from 'antd' import { useWatch } from 'antd/lib/form/Form' import { Callout } from 'components/Callout/Callout' -import { useLockPageRulesWrapper } from 'components/Create/hooks/useLockPageRulesWrapper' import { DurationInput } from 'components/inputs/DurationInput' import { JuiceDatePicker } from 'components/inputs/JuiceDatePicker' import { CREATE_FLOW } from 'constants/fathomEvents' import { trackFathomGoal } from 'lib/fathom' import moment from 'moment' import Link from 'next/link' +import { useLockPageRulesWrapper } from 'packages/v2v3/components/Create/hooks/useLockPageRulesWrapper' import { useContext, useEffect } from 'react' import { useSetCreateFurthestPageReached } from 'redux/hooks/useEditingCreateFurthestPageReached' import { durationMustExistRule } from 'utils/antdRules' diff --git a/src/components/Create/components/pages/FundingCycles/hooks/useFundingCyclesForm.ts b/src/packages/v2v3/components/Create/components/pages/FundingCycles/hooks/useFundingCyclesForm.ts similarity index 100% rename from src/components/Create/components/pages/FundingCycles/hooks/useFundingCyclesForm.ts rename to src/packages/v2v3/components/Create/components/pages/FundingCycles/hooks/useFundingCyclesForm.ts diff --git a/src/components/Create/components/pages/NftRewards/NftRewardsPage.tsx b/src/packages/v2v3/components/Create/components/pages/NftRewards/NftRewardsPage.tsx similarity index 100% rename from src/components/Create/components/pages/NftRewards/NftRewardsPage.tsx rename to src/packages/v2v3/components/Create/components/pages/NftRewards/NftRewardsPage.tsx diff --git a/src/components/Create/components/pages/NftRewards/hooks/useCreateFlowNftRewardsForm.ts b/src/packages/v2v3/components/Create/components/pages/NftRewards/hooks/useCreateFlowNftRewardsForm.ts similarity index 100% rename from src/components/Create/components/pages/NftRewards/hooks/useCreateFlowNftRewardsForm.ts rename to src/packages/v2v3/components/Create/components/pages/NftRewards/hooks/useCreateFlowNftRewardsForm.ts diff --git a/src/components/Create/components/pages/PayoutsPage/PayoutsPage.tsx b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/PayoutsPage.tsx similarity index 100% rename from src/components/Create/components/pages/PayoutsPage/PayoutsPage.tsx rename to src/packages/v2v3/components/Create/components/pages/PayoutsPage/PayoutsPage.tsx diff --git a/src/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx similarity index 100% rename from src/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx rename to src/packages/v2v3/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx diff --git a/src/components/Create/components/pages/PayoutsPage/components/RadioCard.tsx b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/components/RadioCard.tsx similarity index 92% rename from src/components/Create/components/pages/PayoutsPage/components/RadioCard.tsx rename to src/packages/v2v3/components/Create/components/pages/PayoutsPage/components/RadioCard.tsx index 4f7e7965e9..c46dbe67be 100644 --- a/src/components/Create/components/pages/PayoutsPage/components/RadioCard.tsx +++ b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/components/RadioCard.tsx @@ -1,4 +1,4 @@ -import { CheckedCircle } from 'components/Create/components/Selection/components/CheckedCircle' +import { CheckedCircle } from 'packages/v2v3/components/Create/components/Selection/components/CheckedCircle' import { ReactNode } from 'react' import { twMerge } from 'tailwind-merge' diff --git a/src/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx similarity index 100% rename from src/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx rename to src/packages/v2v3/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx diff --git a/src/components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections.ts b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections.ts similarity index 73% rename from src/components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections.ts rename to src/packages/v2v3/components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections.ts index 20781491af..993611ee99 100644 --- a/src/components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections.ts +++ b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections.ts @@ -1,5 +1,5 @@ -import { determineAvailablePayoutsSelections } from 'components/Create/utils/determineAvailablePayoutsSelections' import { PayoutsSelection } from 'models/payoutsSelection' +import { determineAvailablePayoutsSelections } from 'packages/v2v3/components/Create/utils/determineAvailablePayoutsSelections' import { useEditingDistributionLimit } from 'redux/hooks/useEditingDistributionLimit' export const useAvailablePayoutsSelections = (): Set => { diff --git a/src/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts b/src/packages/v2v3/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts similarity index 100% rename from src/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts rename to src/packages/v2v3/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts diff --git a/src/components/Create/components/pages/ProjectDetails/ProjectDetailsPage.tsx b/src/packages/v2v3/components/Create/components/pages/ProjectDetails/ProjectDetailsPage.tsx similarity index 98% rename from src/components/Create/components/pages/ProjectDetails/ProjectDetailsPage.tsx rename to src/packages/v2v3/components/Create/components/pages/ProjectDetails/ProjectDetailsPage.tsx index 707b5b4ebe..ae8d94bb48 100644 --- a/src/components/Create/components/pages/ProjectDetails/ProjectDetailsPage.tsx +++ b/src/packages/v2v3/components/Create/components/pages/ProjectDetails/ProjectDetailsPage.tsx @@ -2,7 +2,6 @@ import { RightOutlined } from '@ant-design/icons' import { t, Trans } from '@lingui/macro' import { Col, Form, Row } from 'antd' import { Callout } from 'components/Callout/Callout' -import { useLockPageRulesWrapper } from 'components/Create/hooks/useLockPageRulesWrapper' import { FormItems } from 'components/formItems' import { EthAddressInput } from 'components/inputs/EthAddressInput' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' @@ -15,6 +14,7 @@ import { constants } from 'ethers' import { useWallet } from 'hooks/Wallet' import { trackFathomGoal } from 'lib/fathom' import Link from 'next/link' +import { useLockPageRulesWrapper } from 'packages/v2v3/components/Create/hooks/useLockPageRulesWrapper' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' import { V2V3_CURRENCY_ETH, diff --git a/src/components/Create/components/pages/ProjectDetails/hooks/useProjectDetailsForm.ts b/src/packages/v2v3/components/Create/components/pages/ProjectDetails/hooks/useProjectDetailsForm.ts similarity index 100% rename from src/components/Create/components/pages/ProjectDetails/hooks/useProjectDetailsForm.ts rename to src/packages/v2v3/components/Create/components/pages/ProjectDetails/hooks/useProjectDetailsForm.ts diff --git a/src/components/Create/components/pages/ProjectToken/ProjectTokenPage.tsx b/src/packages/v2v3/components/Create/components/pages/ProjectToken/ProjectTokenPage.tsx similarity index 100% rename from src/components/Create/components/pages/ProjectToken/ProjectTokenPage.tsx rename to src/packages/v2v3/components/Create/components/pages/ProjectToken/ProjectTokenPage.tsx diff --git a/src/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/CustomTokenSettings.tsx b/src/packages/v2v3/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/CustomTokenSettings.tsx similarity index 98% rename from src/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/CustomTokenSettings.tsx rename to src/packages/v2v3/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/CustomTokenSettings.tsx index e922dd5b2f..1a245d56ef 100644 --- a/src/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/CustomTokenSettings.tsx +++ b/src/packages/v2v3/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/CustomTokenSettings.tsx @@ -1,7 +1,6 @@ import { t, Trans } from '@lingui/macro' import { Divider, Form } from 'antd' import { Callout } from 'components/Callout/Callout' -import { formatFundingCycleDuration } from 'components/Create/utils/formatFundingCycleDuration' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' import { JuiceSwitch } from 'components/inputs/JuiceSwitch' import NumberSlider from 'components/inputs/NumberSlider' @@ -14,6 +13,7 @@ import { } from 'components/strings' import { TokenRedemptionRateGraph } from 'components/TokenRedemptionRateGraph/TokenRedemptionRateGraph' import useMobile from 'hooks/useMobile' +import { formatFundingCycleDuration } from 'packages/v2v3/components/Create/utils/formatFundingCycleDuration' import { ReservedTokensList } from 'packages/v2v3/components/shared/ReservedTokensList' import { MAX_DISTRIBUTION_LIMIT, MAX_MINT_RATE } from 'packages/v2v3/utils/math' import { useAppSelector } from 'redux/hooks/useAppSelector' diff --git a/src/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/ReservedTokenRateCallout.tsx b/src/packages/v2v3/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/ReservedTokenRateCallout.tsx similarity index 100% rename from src/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/ReservedTokenRateCallout.tsx rename to src/packages/v2v3/components/Create/components/pages/ProjectToken/components/CustomTokenSettings/ReservedTokenRateCallout.tsx diff --git a/src/components/Create/components/pages/ProjectToken/components/DefaultSettings.tsx b/src/packages/v2v3/components/Create/components/pages/ProjectToken/components/DefaultSettings.tsx similarity index 100% rename from src/components/Create/components/pages/ProjectToken/components/DefaultSettings.tsx rename to src/packages/v2v3/components/Create/components/pages/ProjectToken/components/DefaultSettings.tsx diff --git a/src/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts b/src/packages/v2v3/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts similarity index 100% rename from src/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts rename to src/packages/v2v3/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts diff --git a/src/components/Create/components/pages/ReconfigurationRules/ReconfigurationRulesPage.tsx b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/ReconfigurationRulesPage.tsx similarity index 95% rename from src/components/Create/components/pages/ReconfigurationRules/ReconfigurationRulesPage.tsx rename to src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/ReconfigurationRulesPage.tsx index 7c34ac7671..bf24fbeb3a 100644 --- a/src/components/Create/components/pages/ReconfigurationRules/ReconfigurationRulesPage.tsx +++ b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/ReconfigurationRulesPage.tsx @@ -2,8 +2,6 @@ import { t, Trans } from '@lingui/macro' import { Form } from 'antd' import { useWatch } from 'antd/lib/form/Form' import { Callout } from 'components/Callout/Callout' -import { Selection } from 'components/Create/components/Selection/Selection' -import { useAvailableReconfigurationStrategies } from 'components/Create/hooks/useAvailableReconfigurationStrategies' import { JuiceSwitch } from 'components/inputs/JuiceSwitch' import { CONTROLLER_CONFIG_EXPLANATION, @@ -18,6 +16,8 @@ import { CREATE_FLOW } from 'constants/fathomEvents' import { FEATURE_FLAGS } from 'constants/featureFlags' import { readNetwork } from 'constants/networks' import { trackFathomGoal } from 'lib/fathom' +import { Selection } from 'packages/v2v3/components/Create/components/Selection/Selection' +import { useAvailableReconfigurationStrategies } from 'packages/v2v3/components/Create/hooks/useAvailableReconfigurationStrategies' import { useContext } from 'react' import { useSetCreateFurthestPageReached } from 'redux/hooks/useEditingCreateFurthestPageReached' import { featureFlagEnabled } from 'utils/featureFlags' diff --git a/src/components/Create/components/pages/ReconfigurationRules/components/CustomRuleCard.tsx b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/components/CustomRuleCard.tsx similarity index 91% rename from src/components/Create/components/pages/ReconfigurationRules/components/CustomRuleCard.tsx rename to src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/components/CustomRuleCard.tsx index bfa7da4dd8..65204798ab 100644 --- a/src/components/Create/components/pages/ReconfigurationRules/components/CustomRuleCard.tsx +++ b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/components/CustomRuleCard.tsx @@ -1,7 +1,7 @@ import { t } from '@lingui/macro' import { Form } from 'antd' -import { Selection } from 'components/Create/components/Selection/Selection' import { CustomStrategyInput } from 'components/inputs/ReconfigurationStrategy/CustomStrategyInput' +import { Selection } from 'packages/v2v3/components/Create/components/Selection/Selection' import { inputMustBeEthAddressRule, inputMustExistRule } from 'utils/antdRules' export const CustomRuleCard = () => { diff --git a/src/components/Create/components/pages/ReconfigurationRules/components/RuleCard.tsx b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/components/RuleCard.tsx similarity index 74% rename from src/components/Create/components/pages/ReconfigurationRules/components/RuleCard.tsx rename to src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/components/RuleCard.tsx index e2065dfdba..897f28df90 100644 --- a/src/components/Create/components/pages/ReconfigurationRules/components/RuleCard.tsx +++ b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/components/RuleCard.tsx @@ -1,7 +1,7 @@ -import { CreateBadge } from 'components/Create/components/CreateBadge' -import { Selection } from 'components/Create/components/Selection/Selection' -import { AvailableReconfigurationStrategy } from 'components/Create/hooks/useAvailableReconfigurationStrategies' import EthereumAddress from 'components/EthereumAddress' +import { CreateBadge } from 'packages/v2v3/components/Create/components/CreateBadge' +import { Selection } from 'packages/v2v3/components/Create/components/Selection/Selection' +import { AvailableReconfigurationStrategy } from 'packages/v2v3/components/Create/hooks/useAvailableReconfigurationStrategies' export const RuleCard = ({ strategy, diff --git a/src/components/Create/components/pages/ReconfigurationRules/hooks/useReconfigurationRulesForm.ts b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/hooks/useReconfigurationRulesForm.ts similarity index 97% rename from src/components/Create/components/pages/ReconfigurationRules/hooks/useReconfigurationRulesForm.ts rename to src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/hooks/useReconfigurationRulesForm.ts index bd977da8d8..23434ae661 100644 --- a/src/components/Create/components/pages/ReconfigurationRules/hooks/useReconfigurationRulesForm.ts +++ b/src/packages/v2v3/components/Create/components/pages/ReconfigurationRules/hooks/useReconfigurationRulesForm.ts @@ -1,8 +1,8 @@ import { Form } from 'antd' import { useForm } from 'antd/lib/form/Form' -import { useAvailableReconfigurationStrategies } from 'components/Create/hooks/useAvailableReconfigurationStrategies' import { readNetwork } from 'constants/networks' import { ReconfigurationStrategy } from 'models/reconfigurationStrategy' +import { useAvailableReconfigurationStrategies } from 'packages/v2v3/components/Create/hooks/useAvailableReconfigurationStrategies' import { useEffect, useMemo } from 'react' import { useAppDispatch } from 'redux/hooks/useAppDispatch' import { useAppSelector } from 'redux/hooks/useAppSelector' diff --git a/src/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx similarity index 98% rename from src/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx index f4a1e81c33..12b411967e 100644 --- a/src/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx +++ b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx @@ -2,7 +2,6 @@ import { CheckCircleFilled } from '@ant-design/icons' import { Trans } from '@lingui/macro' import { Checkbox, Form } from 'antd' import { Callout } from 'components/Callout/Callout' -import { useDeployProject } from 'components/Create/hooks/DeployProject/useDeployProject' import ExternalLink from 'components/ExternalLink' import TransactionModal from 'components/modals/TransactionModal' import { TERMS_OF_SERVICE_URL } from 'constants/links' @@ -11,6 +10,7 @@ import { emitConfirmationDeletionModal } from 'hooks/emitConfirmationDeletionMod import useMobile from 'hooks/useMobile' import { useModal } from 'hooks/useModal' import { useRouter } from 'next/router' +import { useDeployProject } from 'packages/v2v3/components/Create/hooks/DeployProject/useDeployProject' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { useDispatch } from 'react-redux' import { useAppSelector } from 'redux/hooks/useAppSelector' diff --git a/src/components/Create/components/pages/ReviewDeploy/components/DeploySuccess.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/DeploySuccess.tsx similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/DeploySuccess.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/DeploySuccess.tsx diff --git a/src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/FundingConfigurationReview.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/FundingConfigurationReview.tsx similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/FundingConfigurationReview.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/FundingConfigurationReview.tsx diff --git a/src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts similarity index 90% rename from src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts index 99ccac1c6d..83b7f6a8e3 100644 --- a/src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts +++ b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts @@ -1,7 +1,7 @@ import { t } from '@lingui/macro' -import { useAvailablePayoutsSelections } from 'components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections' -import { formatFundingCycleDuration } from 'components/Create/utils/formatFundingCycleDuration' import moment from 'moment' +import { useAvailablePayoutsSelections } from 'packages/v2v3/components/Create/components/pages/PayoutsPage/hooks/useAvailablePayoutsSelections' +import { formatFundingCycleDuration } from 'packages/v2v3/components/Create/utils/formatFundingCycleDuration' import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { useCallback, useMemo } from 'react' diff --git a/src/components/Create/components/pages/ReviewDeploy/components/ProjectDetailsReview/ProjectDetailsReview.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ProjectDetailsReview/ProjectDetailsReview.tsx similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/ProjectDetailsReview/ProjectDetailsReview.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ProjectDetailsReview/ProjectDetailsReview.tsx diff --git a/src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/ProjectTokenReview.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/ProjectTokenReview.tsx similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/ProjectTokenReview.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/ProjectTokenReview.tsx diff --git a/src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts diff --git a/src/components/Create/components/pages/ReviewDeploy/components/ReviewDescription.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ReviewDescription.tsx similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/ReviewDescription.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/ReviewDescription.tsx diff --git a/src/components/Create/components/pages/ReviewDeploy/components/RewardsReview/RewardsReview.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/RewardsReview/RewardsReview.tsx similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/RewardsReview/RewardsReview.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/RewardsReview/RewardsReview.tsx diff --git a/src/components/Create/components/pages/ReviewDeploy/components/RulesReview/RulesReview.tsx b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/RulesReview/RulesReview.tsx similarity index 100% rename from src/components/Create/components/pages/ReviewDeploy/components/RulesReview/RulesReview.tsx rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/RulesReview/RulesReview.tsx diff --git a/src/components/Create/components/pages/ReviewDeploy/components/RulesReview/hooks/useRulesReview.ts b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/RulesReview/hooks/useRulesReview.ts similarity index 94% rename from src/components/Create/components/pages/ReviewDeploy/components/RulesReview/hooks/useRulesReview.ts rename to src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/RulesReview/hooks/useRulesReview.ts index 898398e828..d80302184d 100644 --- a/src/components/Create/components/pages/ReviewDeploy/components/RulesReview/hooks/useRulesReview.ts +++ b/src/packages/v2v3/components/Create/components/pages/ReviewDeploy/components/RulesReview/hooks/useRulesReview.ts @@ -1,5 +1,5 @@ -import { useAvailableReconfigurationStrategies } from 'components/Create/hooks/useAvailableReconfigurationStrategies' import { readNetwork } from 'constants/networks' +import { useAvailableReconfigurationStrategies } from 'packages/v2v3/components/Create/hooks/useAvailableReconfigurationStrategies' import { useMemo } from 'react' import { useAppSelector } from 'redux/hooks/useAppSelector' import { diff --git a/src/components/Create/components/pages/hooks/useFormDispatchWatch.ts b/src/packages/v2v3/components/Create/components/pages/hooks/useFormDispatchWatch.ts similarity index 100% rename from src/components/Create/components/pages/hooks/useFormDispatchWatch.ts rename to src/packages/v2v3/components/Create/components/pages/hooks/useFormDispatchWatch.ts diff --git a/src/components/Create/hooks/DeployProject/hooks/NFT/useDeployNftProject.ts b/src/packages/v2v3/components/Create/hooks/DeployProject/hooks/NFT/useDeployNftProject.ts similarity index 100% rename from src/components/Create/hooks/DeployProject/hooks/NFT/useDeployNftProject.ts rename to src/packages/v2v3/components/Create/hooks/DeployProject/hooks/NFT/useDeployNftProject.ts diff --git a/src/components/Create/hooks/DeployProject/hooks/NFT/useIsNftProject.ts b/src/packages/v2v3/components/Create/hooks/DeployProject/hooks/NFT/useIsNftProject.ts similarity index 100% rename from src/components/Create/hooks/DeployProject/hooks/NFT/useIsNftProject.ts rename to src/packages/v2v3/components/Create/hooks/DeployProject/hooks/NFT/useIsNftProject.ts diff --git a/src/components/Create/hooks/DeployProject/hooks/NFT/useUploadNftRewards.ts b/src/packages/v2v3/components/Create/hooks/DeployProject/hooks/NFT/useUploadNftRewards.ts similarity index 100% rename from src/components/Create/hooks/DeployProject/hooks/NFT/useUploadNftRewards.ts rename to src/packages/v2v3/components/Create/hooks/DeployProject/hooks/NFT/useUploadNftRewards.ts diff --git a/src/components/Create/hooks/DeployProject/hooks/useDeployStandardProject.ts b/src/packages/v2v3/components/Create/hooks/DeployProject/hooks/useDeployStandardProject.ts similarity index 100% rename from src/components/Create/hooks/DeployProject/hooks/useDeployStandardProject.ts rename to src/packages/v2v3/components/Create/hooks/DeployProject/hooks/useDeployStandardProject.ts diff --git a/src/components/Create/hooks/DeployProject/useDeployProject.ts b/src/packages/v2v3/components/Create/hooks/DeployProject/useDeployProject.ts similarity index 100% rename from src/components/Create/hooks/DeployProject/useDeployProject.ts rename to src/packages/v2v3/components/Create/hooks/DeployProject/useDeployProject.ts diff --git a/src/components/Create/hooks/useAvailableReconfigurationStrategies.ts b/src/packages/v2v3/components/Create/hooks/useAvailableReconfigurationStrategies.ts similarity index 100% rename from src/components/Create/hooks/useAvailableReconfigurationStrategies.ts rename to src/packages/v2v3/components/Create/hooks/useAvailableReconfigurationStrategies.ts diff --git a/src/components/Create/hooks/useLoadInitialStateFromQuery.ts b/src/packages/v2v3/components/Create/hooks/useLoadInitialStateFromQuery.ts similarity index 100% rename from src/components/Create/hooks/useLoadInitialStateFromQuery.ts rename to src/packages/v2v3/components/Create/hooks/useLoadInitialStateFromQuery.ts diff --git a/src/components/Create/hooks/useLockPageRulesWrapper.ts b/src/packages/v2v3/components/Create/hooks/useLockPageRulesWrapper.ts similarity index 100% rename from src/components/Create/hooks/useLockPageRulesWrapper.ts rename to src/packages/v2v3/components/Create/hooks/useLockPageRulesWrapper.ts diff --git a/src/components/Create/utils/determineAvailablePayoutsSelections.ts b/src/packages/v2v3/components/Create/utils/determineAvailablePayoutsSelections.ts similarity index 100% rename from src/components/Create/utils/determineAvailablePayoutsSelections.ts rename to src/packages/v2v3/components/Create/utils/determineAvailablePayoutsSelections.ts diff --git a/src/components/Create/utils/formatFundingCycleDuration.ts b/src/packages/v2v3/components/Create/utils/formatFundingCycleDuration.ts similarity index 100% rename from src/components/Create/utils/formatFundingCycleDuration.ts rename to src/packages/v2v3/components/Create/utils/formatFundingCycleDuration.ts diff --git a/src/components/Create/utils/projectTokenSettingsToReduxFormat.ts b/src/packages/v2v3/components/Create/utils/projectTokenSettingsToReduxFormat.ts similarity index 100% rename from src/components/Create/utils/projectTokenSettingsToReduxFormat.ts rename to src/packages/v2v3/components/Create/utils/projectTokenSettingsToReduxFormat.ts diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayRedeemCard.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayRedeemCard.tsx index 0875bca9b7..934eab322e 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayRedeemCard.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard/PayRedeemCard.tsx @@ -786,8 +786,6 @@ const RedeemModal: React.FC = ({ )} - - {/* View on block explorer */} ) diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/DataSourceListItems/index.tsx b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/DataSourceListItems/index.tsx index fa2ee206eb..2962b62cb6 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/DataSourceListItems/index.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/DataSourceListItems/index.tsx @@ -3,6 +3,7 @@ import { Trans, t } from '@lingui/macro' import { Tooltip } from 'antd' import EthereumAddress from 'components/EthereumAddress' import ExternalLink from 'components/ExternalLink' +import { FundingCycleListItem } from 'components/FundingCycleListItem' import { DATASOURCE_EXPLANATION, NFT_DATASOURCE_EXPLANATION, @@ -16,7 +17,6 @@ import { V2V3FundingCycleMetadata } from 'packages/v2v3/models/fundingCycle' import { useContext } from 'react' import { formatBoolean } from 'utils/format/formatBoolean' import { helpPagePath } from 'utils/helpPagePath' -import { FundingCycleListItem } from '../FundingCycleListItem' function DataSourceAddressValue({ address }: { address: string | undefined }) { const { version } = useContext(JB721DelegateContractsContext) diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/DurationValue.tsx b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/DurationValue.tsx index 878f96d910..246eed3195 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/DurationValue.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/DurationValue.tsx @@ -1,3 +1,4 @@ + import { Trans } from '@lingui/macro' import FundingCycleDetailWarning from 'components/Project/FundingCycleDetailWarning' import { FUNDING_CYCLE_WARNING_TEXT } from 'constants/fundingWarningText' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/index.tsx b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/index.tsx index 9884e1891b..6b6ca096d7 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/index.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/index.tsx @@ -1,5 +1,6 @@ import { t } from '@lingui/macro' import { Tooltip } from 'antd' +import { FundingCycleListItem } from 'components/FundingCycleListItem' import { DISTRIBUTION_LIMIT_EXPLANATION, RECONFIG_RULES_EXPLANATION, @@ -17,7 +18,6 @@ import { V2V3CurrencyName } from 'packages/v2v3/utils/currency' import { getUnsafeV2V3FundingCycleProperties } from 'packages/v2v3/utils/fundingCycle' import { useContext } from 'react' import { formatDate, formatDateToUTC } from 'utils/format/formatDate' -import { FundingCycleListItem } from '../FundingCycleListItem' import { BallotStrategyValue } from '../RulesListItems/BallotStrategyValue' import { DistributionLimitValue } from './DistributionLimitValue' import { DurationValue } from './DurationValue' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/RulesListItems/index.tsx b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/RulesListItems/index.tsx index eb26bfffda..f97b8a806f 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/RulesListItems/index.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/RulesListItems/index.tsx @@ -1,4 +1,5 @@ import { t } from '@lingui/macro' +import { FundingCycleListItem } from 'components/FundingCycleListItem' import { CONTROLLER_CONFIG_EXPLANATION, CONTROLLER_MIGRATION_EXPLANATION, @@ -9,7 +10,6 @@ import { import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { V2V3FundingCycleMetadata } from 'packages/v2v3/models/fundingCycle' import { useContext } from 'react' -import { FundingCycleListItem } from '../FundingCycleListItem' import { AllowedValue } from './AllowedValue' import { HoldFeesValue } from './HoldFeesValue' import { PausePayValue } from './PausePayValue' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/TokenListItems/index.tsx b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/TokenListItems/index.tsx index ef254ad3c8..34737d7a0d 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/TokenListItems/index.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/TokenListItems/index.tsx @@ -1,4 +1,5 @@ import { t } from '@lingui/macro' +import { FundingCycleListItem } from 'components/FundingCycleListItem' import { CONTRIBUTOR_RATE_EXPLANATION, DISCOUNT_RATE_EXPLANATION, @@ -13,7 +14,6 @@ import { V2V3FundingCycle, V2V3FundingCycleMetadata, } from 'packages/v2v3/models/fundingCycle' -import { FundingCycleListItem } from '../FundingCycleListItem' import { BigNumber } from 'ethers' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/index.tsx b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/index.tsx index 0aecb20bef..610d580e0d 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/index.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/index.tsx @@ -5,9 +5,9 @@ import { V2V3FundingCycleMetadata, } from 'packages/v2v3/models/fundingCycle' +import { FundingCycleDetailsRow } from 'components/FundingCycleDetailsRow' import { isZeroAddress } from 'utils/address' import { DataSourceListItems } from './DataSourceListItems' -import { FundingCycleDetailsRow } from './FundingCycleDetailsRow' import { FundingCycleListItems } from './FundingCycleListItems' import { RulesListItems } from './RulesListItems' import { TokenListItems } from './TokenListItems' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/modals/DistributePayoutsModal.tsx b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/modals/DistributePayoutsModal.tsx index ac8f64c150..9c965da2fd 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/modals/DistributePayoutsModal.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/modals/DistributePayoutsModal.tsx @@ -12,8 +12,7 @@ import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectCo import { useDistributePayoutsTx } from 'packages/v2v3/hooks/transactor/useDistributePayouts' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' import { - V2V3_CURRENCY_USD, - V2V3CurrencyName, + V2V3CurrencyName } from 'packages/v2v3/utils/currency' import { useContext, useEffect, useState } from 'react' import { fromWad, parseWad } from 'utils/format/formatNumber' @@ -60,13 +59,6 @@ export default function DistributePayoutsModal({ async function executeDistributePayoutsTx() { if (!distributionLimitCurrency || !distributionAmount) return - const minAmount = ( - distributionLimitCurrency.eq(V2V3_CURRENCY_USD) - ? converter.usdToWei(distributionAmount) - : parseWad(distributionAmount) - )?.sub(1e12) // Arbitrary value subtracted - if (!minAmount) return - setLoading(true) const txSuccessful = await distributePayoutsTx( diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/DetailsSectionDiff.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/DetailsSectionDiff.tsx index 695c7dac8f..f24754dd42 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/DetailsSectionDiff.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/DetailsSectionDiff.tsx @@ -1,5 +1,5 @@ import { Trans, t } from '@lingui/macro' -import { FundingCycleListItem } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItem' +import { FundingCycleListItem } from 'components/FundingCycleListItem' import { DurationValue } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/DurationValue' import { BallotStrategyValue } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/RulesListItems/BallotStrategyValue' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/PayoutsSectionDiff.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/PayoutsSectionDiff.tsx index 91b53030c4..1b0e488e76 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/PayoutsSectionDiff.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/PayoutsSectionDiff.tsx @@ -1,6 +1,6 @@ import { BigNumber } from '@ethersproject/bignumber' import { Trans, t } from '@lingui/macro' -import { FundingCycleListItem } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItem' +import { FundingCycleListItem } from 'components/FundingCycleListItem' import { DistributionLimitValue } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItems/DistributionLimitValue' import DiffedSplitList from 'packages/v2v3/components/shared/DiffedSplits/DiffedSplitList' import { getV2V3CurrencyOption } from 'packages/v2v3/utils/currency' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx index 030aeea4c2..f2b364fa8f 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx @@ -1,9 +1,9 @@ import { Trans, t } from '@lingui/macro' import { Form } from 'antd' import { useWatch } from 'antd/lib/form/Form' -import { CreateCollapse } from 'components/Create/components/CreateCollapse/CreateCollapse' import { JuiceTextArea } from 'components/inputs/JuiceTextArea' import TransactionModal from 'components/modals/TransactionModal' +import { CreateCollapse } from 'packages/v2v3/components/Create/components/CreateCollapse/CreateCollapse' import { useState } from 'react' import { TransactionSuccessModal } from '../../../TransactionSuccessModal' import { useReconfigureFundingCycle } from '../../../hooks/useReconfigureFundingCycle' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/TokensSectionDiff.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/TokensSectionDiff.tsx index cfd026ec1f..17f13f2df9 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/TokensSectionDiff.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/TokensSectionDiff.tsx @@ -1,6 +1,6 @@ import { BigNumber } from '@ethersproject/bignumber' import { Trans, t } from '@lingui/macro' -import { FundingCycleListItem } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/FundingCycleListItem' +import { FundingCycleListItem } from 'components/FundingCycleListItem' import { MintRateValue } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/TokenListItems/MintRateValue' import { ReservedRateValue } from 'packages/v2v3/components/V2V3Project/V2V3FundingCycleSection/FundingCycleDetails/TokenListItems/ReservedRateValue' import DiffedSplitList from 'packages/v2v3/components/shared/DiffedSplits/DiffedSplitList' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx index 9b708bba3f..d9aaddb0e7 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx @@ -3,9 +3,10 @@ import { Form } from 'antd' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' import { MAX_MINT_RATE } from 'packages/v2v3/utils/math' +// Note: "issuanceRate" = "mintRate" export function MintRateField() { return ( - + !editingData?.projectOwner && editingData?.projectId && - editingData.projectId !== BigNumber.from(0).toHexString(), + editingData.projectId !== '0' && + editingData.projectId !== '0x00' && + editingData.projectId !== 0n.toString(), [editingData], ) diff --git a/src/packages/v4/components/Allocation/Allocation.tsx b/src/packages/v4/components/Allocation/Allocation.tsx index 243ba8b1a0..a63ed1144e 100644 --- a/src/packages/v4/components/Allocation/Allocation.tsx +++ b/src/packages/v4/components/Allocation/Allocation.tsx @@ -3,6 +3,7 @@ import { FormItemInput } from 'models/formItemInput' import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption' import { createContext, useContext } from 'react' import { AllocationItem } from './AllocationItem' +import { AllocationList } from './AllocationList' import { useAllocation } from './hooks/useAllocation' export type AllocationSplit = Split & { id: string } @@ -57,6 +58,7 @@ export const Allocation: React.FC< React.PropsWithChildren> > & { Item: typeof AllocationItem + List: typeof AllocationList useAllocationInstance: typeof useAllocationInstance } = ({ totalAllocationAmount, @@ -85,4 +87,5 @@ export const Allocation: React.FC< } Allocation.useAllocationInstance = useAllocationInstance +Allocation.List = AllocationList Allocation.Item = AllocationItem diff --git a/src/packages/v4/components/Allocation/AllocationList.tsx b/src/packages/v4/components/Allocation/AllocationList.tsx new file mode 100644 index 0000000000..fb22125b51 --- /dev/null +++ b/src/packages/v4/components/Allocation/AllocationList.tsx @@ -0,0 +1,302 @@ +import { t, Trans } from '@lingui/macro' +import { CreateButton } from 'components/buttons/CreateButton/CreateButton' +import { BigNumber } from 'ethers' +import { useModal } from 'hooks/useModal' + +import { SplitPortion, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' +import round from 'lodash/round' +import { amountFromPercent } from 'packages/v4/utils/distributions' +import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math' +import { ReactNode, useCallback, useState } from 'react' +import { twMerge } from 'tailwind-merge' +import { fromWad } from 'utils/format/formatNumber' +import { roundIfCloseToNextInteger } from 'utils/math' +import { zeroAddress } from 'viem' +import { + AddEditAllocationModal, + AddEditAllocationModalEntity, +} from './AddEditAllocationModal' +import { Allocation, AllocationSplit } from './Allocation' + +type AddButtonSize = 'small' | 'large' + +export const allocationId = ( + beneficiary: string, + projectIdHex: string | undefined, +) => { + const hasProjectId = Boolean(projectIdHex && projectIdHex !== '0x00') + return `${beneficiary}${hasProjectId ? `-${projectIdHex}` : ''}` +} + +export const AllocationList = ({ + allocationName = t`allocation`, + isEditable = true, + addButtonSize, + availableModes, + children, +}: { + allocationName?: string + isEditable?: boolean + addButtonSize?: AddButtonSize + availableModes: Set<'amount' | 'percentage'> + children: ( + modalOperations: ReturnType, + allocationOperations: ReturnType< + typeof Allocation.useAllocationInstance + > & { setSelectedAllocation: (a: AllocationSplit | undefined) => void }, + ) => ReactNode +}) => { + const allocationInstance = Allocation.useAllocationInstance() + const { + setAllocations, + upsertAllocation, + setTotalAllocationAmount, + totalAllocationAmount, + allocations, + } = allocationInstance + + const [selectedAllocation, _setSelectedAllocation] = + useState() + const modal = useModal() + + const setSelectedAllocation = useCallback( + (value: AllocationSplit | undefined) => { + const entity = value + ? allocationToEntity(value, totalAllocationAmount, availableModes) + : undefined + _setSelectedAllocation(entity) + }, + [totalAllocationAmount, availableModes], + ) + + const removeAllocation = useCallback( + (id: string) => { + const allocation = allocations.find(a => a.id === id) + if (!allocation) return + + if ( + !totalAllocationAmount || + totalAllocationAmount === MAX_PAYOUT_LIMIT + ) { + allocationInstance.removeAllocation(id) + return + } + + const totalAmount = parseFloat(fromWad(totalAllocationAmount)) + const removedAmount = (allocation.percent.formatPercentage() / 100) * totalAmount + + const totalAmountAfterRemoval = roundIfCloseToNextInteger( + totalAmount - removedAmount, + ) + + const adjustedAllocations = allocations + .filter(a => a.id !== allocation.id) + .map(alloc => { + const currentAmount = amountFromPercent({ + percent: alloc.percent.formatPercentage(), + amount: totalAmount.toString(), + }) + const newPercent = (currentAmount / totalAmountAfterRemoval) * 100 + return { ...alloc, percent: new SplitPortion(newPercent) } + }) + + setAllocations(adjustedAllocations) + setTotalAllocationAmount?.(BigInt(totalAmountAfterRemoval)) + }, + [ + allocationInstance, + allocations, + setAllocations, + setTotalAllocationAmount, + totalAllocationAmount, + ], + ) + + const onModalOk = useCallback( + (result: AddEditAllocationModalEntity) => { + const isEditing = !!selectedAllocation + if (result.projectOwner) { + if (!setTotalAllocationAmount) { + throw new Error( + 'Allocation amount passed but AllocationList has no totalAllocationAmount set', + ) + } + + const originalTotal = parseFloat( + fromWad(totalAllocationAmount ?? BigNumber.from(0)), + ) + let totalAmount = originalTotal + + if (isEditing) { + if (!selectedAllocation.projectOwner) + throw new Error('selected allocation is not owner') + totalAmount = Math.max( + 0, + totalAmount - parseFloat(selectedAllocation.amount), + ) + } + + totalAmount += parseFloat(result.amount) + + const adjustedAllocations = allocations.map(alloc => { + const currentAmount = amountFromPercent({ + percent: alloc.percent.formatPercentage(), + amount: originalTotal.toString(), + }) + const newPercent = (currentAmount / totalAmount) * 100 + return { ...alloc, percent: new SplitPortion(newPercent) } + }) + setAllocations(adjustedAllocations) + setTotalAllocationAmount(BigInt(totalAmount)) + } else { + if (result.amount.isPercent) { + const allocation = entityToAllocation(result) + upsertAllocation(allocation) + } else { + if (!totalAllocationAmount) { + throw new Error( + 'Allocation amount passed but AllocationList has no totalAllocationAmount set', + ) + } + + let total = Number(totalAllocationAmount) + + // checks original ID + const existingAllocation = isEditing + ? allocations.find(a => a.id === result.previousId) + : undefined + + + if (existingAllocation) { + const existingAmount = + (existingAllocation.percent.formatPercentage() / 100.0) * total + total = Math.max(0, total - existingAmount) + } + + const newOrEditedAllocation = entityToAllocation(result) + + let newAllocationInserted = false + const adjustedAllocations: AllocationSplit[] = allocations.map( + allocation => { + // skip if existing + if (allocation.id === existingAllocation?.id) { + newAllocationInserted = true + return newOrEditedAllocation + } + const currentAmount = amountFromPercent({ + percent: allocation.percent.formatPercentage(), + amount: total.toString(), + }) + + const newPercent = + (currentAmount / total) * 100 + return { + ...allocation, + percent: new SplitPortion(newPercent), + } + }, + ) + + setAllocations([ + ...adjustedAllocations, + // Only insert new if it wasn't added previously + ...(newAllocationInserted ? [] : [newOrEditedAllocation]), + ]) + setTotalAllocationAmount?.(BigInt(total)) + } + } + + modal.close() + setSelectedAllocation(undefined) + }, + [ + allocations, + modal, + selectedAllocation, + setAllocations, + setSelectedAllocation, + setTotalAllocationAmount, + totalAllocationAmount, + upsertAllocation, + ], + ) + const onModalCancel = useCallback(() => { + modal.close() + setSelectedAllocation(undefined) + }, [modal, setSelectedAllocation]) + + return ( + <> +
+ {children(modal, { + ...allocationInstance, + setSelectedAllocation, + removeAllocation, + })} + {isEditable && ( + + Add {allocationName ? allocationName : ''} + + )} +
+ + + ) +} + +const entityToAllocation = ( + entity: AddEditAllocationModalEntity & { projectOwner: false }, +): AllocationSplit => { + const allocationProps = { + id: allocationId(entity.beneficiary!, entity.projectId ? `pid-${entity.projectId}` : undefined), + beneficiary: entity.beneficiary ?? zeroAddress, + projectId: BigInt(entity.projectId ?? 0), + lockedUntil: entity.lockedUntil ?? 0, + percent: entity.amount.value ? parseFloat(entity.amount.value) : 0, + preferAddToBalance: false, + hook: zeroAddress, + } + const percent = allocationProps.percent + const percentPPB = round((percent / 100) * SPLITS_TOTAL_PERCENT) + + return { ...allocationProps, percent: new SplitPortion(percentPPB) } +} + +const allocationToEntity = ( + alloc: AllocationSplit, + totalAllocationAmount: bigint | undefined, + availableModes: Set<'amount' | 'percentage'>, +): AddEditAllocationModalEntity & { projectOwner: false } => { + const isPercent = + !totalAllocationAmount || + totalAllocationAmount === MAX_PAYOUT_LIMIT || + !availableModes.has('amount') // override if amount not available in the instance + let amount = { value: alloc.percent.formatPercentage().toString(), isPercent } + + if (!isPercent && totalAllocationAmount) { + const total = parseFloat(fromWad(totalAllocationAmount)) + const a = (alloc.percent.formatPercentage() / 100.0) * total + amount = { value: a.toString(), isPercent: false } + } + return { + projectOwner: false, + projectId: alloc.projectId.toString(), + beneficiary: alloc.beneficiary, + amount, + lockedUntil: alloc.lockedUntil, + } +} diff --git a/src/packages/v4/components/Allocation/components/AllocationItemTitle.tsx b/src/packages/v4/components/Allocation/components/AllocationItemTitle.tsx new file mode 100644 index 0000000000..a6c33083bf --- /dev/null +++ b/src/packages/v4/components/Allocation/components/AllocationItemTitle.tsx @@ -0,0 +1,34 @@ +import { LockFilled } from '@ant-design/icons' +import { t } from '@lingui/macro' +import { Tooltip } from 'antd' +import EthereumAddress from 'components/EthereumAddress' +import { isProjectSplit } from 'packages/v4/utils/v4Splits' +import { formatDate } from 'utils/format/formatDate' +import V4ProjectHandleLink from '../../V4ProjectHandleLink' +import { AllocationSplit } from '../Allocation' + +export function AllocationItemTitle({ + allocation, +}: { + allocation: AllocationSplit +}) { + return ( +
+ {isProjectSplit(allocation) && allocation.projectId ? ( + + ) : ( + + )} + {!!allocation.lockedUntil && ( + + + + )} +
+ ) +} diff --git a/src/packages/v4/components/Allocation/hooks/useFundingTargetType.ts b/src/packages/v4/components/Allocation/hooks/useFundingTargetType.ts new file mode 100644 index 0000000000..505eb8fe7d --- /dev/null +++ b/src/packages/v4/components/Allocation/hooks/useFundingTargetType.ts @@ -0,0 +1,13 @@ +import { FundingTargetType } from 'models/fundingTargetType' +import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math' +import { useMemo } from 'react' + +export const useFundingTargetType = ( + fundingTargetAmount: bigint | undefined, +): FundingTargetType | undefined => { + return useMemo(() => { + if (!fundingTargetAmount || fundingTargetAmount === 0n) return undefined + if (fundingTargetAmount === MAX_PAYOUT_LIMIT) return 'infinite' + return 'specific' + }, [fundingTargetAmount]) +} diff --git a/src/packages/v4/components/Allocation/hooks/usePayoutSplitAmountPercentage.ts b/src/packages/v4/components/Allocation/hooks/usePayoutSplitAmountPercentage.ts new file mode 100644 index 0000000000..66f6c0a51f --- /dev/null +++ b/src/packages/v4/components/Allocation/hooks/usePayoutSplitAmountPercentage.ts @@ -0,0 +1,75 @@ +import { useMemo } from 'react' +import { fromWad } from 'utils/format/formatNumber' +import { AllocationSplit } from '../Allocation' +import { useFundingTargetType } from './useFundingTargetType' + +/** + * Hook to calculate the payout split amount percentage. + * + * Using the `allocations`, the id of the allocation (`allocationId) will + * determine the `amount` and `percentage` of the `totalAllocationAmount`. + * + * If no `totalAllocationAmount` exists, only the percentage will be returned. + */ +export const usePayoutSplitAmountPercentage = ({ + allocationId, + allocations, + totalAllocationAmount, +}: { + allocationId?: string + allocations: AllocationSplit[] + totalAllocationAmount?: bigint | undefined +}) => { + const fundingTargetType = useFundingTargetType(totalAllocationAmount) + const hasSpecificFundingTarget = fundingTargetType === 'specific' + + const totalUsedPercent = useMemo( + () => allocations.map(a => a.percent.formatPercentage()).reduce((prev, acc) => prev + acc, 0), + [allocations], + ) + const ownerPercent = 100.0 - totalUsedPercent + + const amount = useMemo(() => { + if (!hasSpecificFundingTarget || !totalAllocationAmount) return undefined + if (!allocationId) { + // If allocation is not passed, assume this is for owner + return (parseFloat(fromWad(totalAllocationAmount)) * ownerPercent) / 100 + } + + const allocation = allocations.find(a => a.id === allocationId) + if (!allocation) { + console.warn( + 'Allocation id passed to usePayoutSplitAmountPercentage with no matching id in allocations', + { allocationId, allocations }, + ) + return NaN + } + return ( + (allocation.percent.formatPercentage() * parseFloat(fromWad(totalAllocationAmount))) / 100 + ) + }, [ + allocationId, + allocations, + totalAllocationAmount, + hasSpecificFundingTarget, + ownerPercent, + ]) + + const percent = useMemo(() => { + if (!allocationId) { + // If allocation is not passed, assume this is for owner + return ownerPercent + } + const allocation = allocations.find(a => a.id === allocationId) + if (!allocation) { + console.warn( + 'Allocation id passed to usePayoutSplitAmountPercentage with no matching id in allocations', + { allocationId, allocations }, + ) + return NaN + } + return allocation.percent.formatPercentage() + }, [allocationId, allocations, ownerPercent]) + + return { amount, percent } +} diff --git a/src/packages/v4/components/PayoutCard/Amount.tsx b/src/packages/v4/components/PayoutCard/Amount.tsx new file mode 100644 index 0000000000..f2638027ac --- /dev/null +++ b/src/packages/v4/components/PayoutCard/Amount.tsx @@ -0,0 +1,65 @@ +import { Parenthesis } from 'components/Parenthesis' +import { PayoutsSelection } from 'models/payoutsSelection' +import { formatCurrencyAmount } from 'packages/v4/utils/formatCurrencyAmount' +import { useMemo } from 'react' +import { formatPercent } from 'utils/format/formatPercent' +import { Allocation } from '../Allocation/Allocation' +import { usePayoutSplitAmountPercentage } from '../Allocation/hooks/usePayoutSplitAmountPercentage' + +export const Amount = ({ + allocationId, // if undefined, assume owner + payoutsSelection, +}: { + allocationId?: string + payoutsSelection: PayoutsSelection +}) => { + const isOwner = allocationId === undefined + + const { allocations, totalAllocationAmount, allocationCurrency } = + Allocation.useAllocationInstance() + + const { amount, percent } = usePayoutSplitAmountPercentage({ + allocationId: allocationId, + allocations, + totalAllocationAmount: totalAllocationAmount, + }) + const formattedAmount = useMemo(() => { + if (amount === undefined) return undefined + let _amount = amount + if (isOwner && amount <= 0) { + _amount = 0 + } + return formatCurrencyAmount({ + amount: _amount, + currency: allocationCurrency, + }) + }, [allocationCurrency, amount, isOwner]) + + const formattedPercent = useMemo(() => { + let _percent = percent + if (isOwner && percent <= 0) { + _percent = 0 + } + return formatPercent(_percent) + }, [isOwner, percent]) + + const [primaryText, secondaryText] = useMemo(() => { + if (payoutsSelection === 'amounts') { + return [formattedAmount, formattedPercent] + } + if (payoutsSelection === 'percentages') { + return [formattedPercent, formattedAmount] + } + + throw new Error('Unexpected end of function') + }, [formattedAmount, formattedPercent, payoutsSelection]) + + return ( +
+ {!!primaryText && primaryText} + + {!!secondaryText && secondaryText} + +
+ ) +} diff --git a/src/packages/v4/components/PayoutCard/OwnerPayoutCard.tsx b/src/packages/v4/components/PayoutCard/OwnerPayoutCard.tsx new file mode 100644 index 0000000000..10444cce2b --- /dev/null +++ b/src/packages/v4/components/PayoutCard/OwnerPayoutCard.tsx @@ -0,0 +1,39 @@ +import { CrownFilled, QuestionCircleOutlined } from '@ant-design/icons' +import { Trans } from '@lingui/macro' +import { Tooltip } from 'antd' +import { PayoutsSelection } from 'models/payoutsSelection' +import { Allocation } from '../Allocation/Allocation' +import { Amount } from './Amount' + +type OwnerPayoutCardProps = { + payoutsSelection: PayoutsSelection +} + +export const OwnerPayoutCard: React.FC< + React.PropsWithChildren +> = props => { + return ( + + Project owner{' '} + + + } + amount={} + extra={ + + Your payouts do not sum to 100%. Because your project is set to + pay out all available ETH, the remainder will go to the project + owner. + + } + > + + + } + /> + ) +} diff --git a/src/packages/v4/components/PayoutCard/PayoutCard.tsx b/src/packages/v4/components/PayoutCard/PayoutCard.tsx new file mode 100644 index 0000000000..f645e286ff --- /dev/null +++ b/src/packages/v4/components/PayoutCard/PayoutCard.tsx @@ -0,0 +1,69 @@ +import { DeleteOutlined } from '@ant-design/icons' +import { Trans } from '@lingui/macro' +import EthereumAddress from 'components/EthereumAddress' +import { DeleteConfirmationModal } from 'components/modals/DeleteConfirmationModal' +import { useModal } from 'hooks/useModal' +import { PayoutsSelection } from 'models/payoutsSelection' +import { + Allocation, + AllocationSplit, +} from 'packages/v2v3/components/shared/Allocation/Allocation' +import { AllocationItemTitle } from 'packages/v2v3/components/shared/Allocation/components/AllocationItemTitle' +import { useCallback } from 'react' +import { stopPropagation } from 'react-stop-propagation' +import { Amount } from './Amount' + +export const PayoutCard = ({ + allocation, + payoutsSelection, + onClick, + onDeleteClick, +}: { + allocation: AllocationSplit + payoutsSelection: PayoutsSelection + onClick?: VoidFunction + onDeleteClick?: VoidFunction +}) => { + const deleteConfirmationModal = useModal() + + const handleDeleteConfirmationModalOk = useCallback(() => { + onDeleteClick?.() + deleteConfirmationModal.close() + }, [deleteConfirmationModal, onDeleteClick]) + return ( + <> + } + amount={ + + } + extra={ + onDeleteClick ? ( + + ) : ( +
+ ) + } + onClick={onClick} + /> + + + This will delete the payout for{' '} + . + + } + /> + + ) +} diff --git a/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/PayProjectModal/hooks/useProjectPaymentTokens.ts b/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/PayProjectModal/hooks/useProjectPaymentTokens.ts index 78ade4f438..cc8599c872 100644 --- a/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/PayProjectModal/hooks/useProjectPaymentTokens.ts +++ b/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/PayProjectModal/hooks/useProjectPaymentTokens.ts @@ -9,6 +9,7 @@ import { useProjectSelector } from 'packages/v4/components/ProjectDashboard/redu import { V4_CURRENCY_USD } from 'packages/v4/utils/currency' import { tokenSymbolText } from 'utils/tokenSymbolText' import { formatUnits } from 'viem' + export const useProjectPaymentTokens = () => { const payAmount = useProjectSelector(state => state.projectCart.payAmount) const { ruleset, rulesetMetadata } = useJBRulesetContext() @@ -19,6 +20,7 @@ export const useProjectPaymentTokens = () => { let payAmountWei = payAmount?.amount if (payAmount?.currency === V4_CURRENCY_USD) { // convert to wei first + // TODO support usd payments } const amountBQuote = diff --git a/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/RedeemConfiguration.tsx b/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/RedeemConfiguration.tsx new file mode 100644 index 0000000000..17048aa8ee --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/RedeemConfiguration.tsx @@ -0,0 +1,256 @@ +import { CheckCircleIcon } from '@heroicons/react/24/outline' +import { t, Trans } from '@lingui/macro' +import { waitForTransactionReceipt } from '@wagmi/core' +import { Button, Tooltip } from 'antd' +import Loading from 'components/Loading' +import { JuiceModal, JuiceModalProps } from 'components/modals/JuiceModal' +import { PV_V4 } from 'constants/pv' +import { useProjectMetadataContext } from 'contexts/ProjectMetadataContext' +import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext' +import { useProjectLogoSrc } from 'hooks/useProjectLogoSrc' +import { useWallet } from 'hooks/Wallet' +import { NATIVE_TOKEN } from 'juice-sdk-core' +import { + useJBContractContext, + useJBTokenContext, + useWriteJbMultiTerminalRedeemTokensOf, +} from 'juice-sdk-react' +import { useETHReceivedFromTokens } from 'packages/v4/hooks/useETHReceivedFromTokens' +import { usePayoutLimit } from 'packages/v4/hooks/usePayoutLimit' +import { V4_CURRENCY_USD } from 'packages/v4/utils/currency' +import { wagmiConfig } from 'packages/v4/wagmiConfig' +import { useCallback, useContext, useMemo, useState } from 'react' +import { emitErrorNotification } from 'utils/notifications' +import { formatEther, parseUnits } from 'viem' +import { EthereumLogo } from './EthereumLogo' +import { PayRedeemInput } from './PayRedeemInput' +type RedeemConfigurationProps = { + userTokenBalance: number | undefined + projectHasErc20Token: boolean +} + +export const RedeemConfiguration: React.FC = ({ + userTokenBalance, + projectHasErc20Token, +}) => { + const { token } = useJBTokenContext() + const tokenSymbol = token?.data?.symbol + const { data: payoutLimit } = usePayoutLimit() + const { projectId, projectMetadata } = useProjectMetadataContext() + const { contracts } = useJBContractContext() + const { addTransaction } = useContext(TxHistoryContext) + + const wallet = useWallet() + // TODO: We should probably break out tokens panel hook into reusable module + const tokenLogo = useProjectLogoSrc({ + projectId, + pv: PV_V4, + uri: projectMetadata?.logoUri, + }) + + const [redeemAmount, setRedeemAmount] = useState() + const [fallbackImage, setFallbackImage] = useState() + const [modalOpen, setModalOpen] = useState(false) + const [redeeming, setRedeeming] = useState(false) + + const redeemAmountWei = parseUnits( + redeemAmount || '0', + token?.data?.decimals ?? 18, + ) + + const ethReceivedFromTokens = useETHReceivedFromTokens(redeemAmountWei) + const tokenFromRedeemAmount = ethReceivedFromTokens + ? formatEther(ethReceivedFromTokens) + : '' + + const { writeContractAsync: writeRedeem } = + useWriteJbMultiTerminalRedeemTokensOf() + const { userAddress } = useWallet() + + const insufficientBalance = useMemo(() => { + if (!userTokenBalance) return false + const amount = Number(redeemAmount || 0) + const balance = userTokenBalance ?? 0 + return amount > balance + }, [redeemAmount, userTokenBalance]) + + const tokenTicker = tokenSymbol || 'TOKENS' + + // 0.5% slippage for USD-denominated tokens + const slippage = + payoutLimit?.currency === V4_CURRENCY_USD + ? ((ethReceivedFromTokens ?? 0n) * 1000n) / 1005n + : ethReceivedFromTokens + + const redeem = useCallback(async () => { + if (!slippage) { + emitErrorNotification('Failed to calculate slippage') + return + } + + if (!contracts.primaryNativeTerminal.data || !projectId) { + emitErrorNotification('Failed to prepare transaction') + return + } + + if (!userAddress) { + emitErrorNotification('No wallet connected') + return + } + + setRedeeming(true) + + const args = [ + userAddress, + BigInt(projectId), + NATIVE_TOKEN, + redeemAmountWei, + 0n, + userAddress, + '0x0', + ] as const + try { + const hash = await writeRedeem({ + address: contracts.primaryNativeTerminal.data, + args, + }) + setModalOpen(true) + + addTransaction?.('Redeem', { hash }) + await waitForTransactionReceipt(wagmiConfig, { + hash, + }) + setRedeeming(false) + } catch (e) { + setRedeeming(false) + setModalOpen(false) + emitErrorNotification((e as unknown as Error).message) + } + }, [ + slippage, + addTransaction, + contracts.primaryNativeTerminal.data, + projectId, + redeemAmountWei, + userAddress, + writeRedeem, + ]) + + return ( + <> +
+
+
+ + {t`You redeem`} + + } + token={{ + balance: userTokenBalance?.toString(), + image: + tokenLogo && !fallbackImage ? ( + Token logo setFallbackImage(true)} + /> + ) : ( + '🧃' + ), + ticker: tokenTicker, + type: projectHasErc20Token ? 'erc20' : 'native', + }} + value={redeemAmount} + onChange={setRedeemAmount} + /> + , + ticker: 'ETH', + type: 'eth', + }} + value={tokenFromRedeemAmount} + /> +
+
+ + +
+ + + ) +} + +const RedeemModal: React.FC = ({ + redeeming, + ...props +}) => { + return ( + +
+ {redeeming ? ( + <> + +

+ Redeeming tokens +

+
+ Your transaction is processing. +
+
+ You can safely close this modal. +
+ + ) : ( + <> +
+
+ +
+
+

+ Success! +

+
+ Your transaction was successful. +
+
+ You can safely close this modal. +
+ + )} +
+
+ ) +} diff --git a/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/V4PayRedeemCard.tsx b/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/V4PayRedeemCard.tsx index 6d665f0d23..fb63e4426d 100644 --- a/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/V4PayRedeemCard.tsx +++ b/src/packages/v4/components/ProjectDashboard/V4PayRedeemCard/V4PayRedeemCard.tsx @@ -11,7 +11,7 @@ import { useProjectDispatch, useProjectSelector } from '../redux/hooks' import { payRedeemActions } from '../redux/payRedeemSlice' import { PayConfiguration } from './PayConfiguration' import { PayProjectModal } from './PayProjectModal/PayProjectModal' -// import { RedeemConfiguration } from './RedeemConfiguration' +import { RedeemConfiguration } from './RedeemConfiguration' type PayRedeemCardProps = { className?: string @@ -34,11 +34,11 @@ export const V4PayRedeemCard: React.FC = ({ // ? parseFloat(panelBalance.replaceAll(',', '')) // : undefined const tokenBalance = 0 // TODO - const redeems = { loading: ruleset.isLoading, enabled: - !((rulesetMetadata.data?.redemptionRate?.value ?? 0n) > 0n) || false, + rulesetMetadata.data?.redemptionRate && + rulesetMetadata.data.redemptionRate.value > 0n, } const weight = ruleset.data?.weight @@ -90,12 +90,12 @@ export const V4PayRedeemCard: React.FC = ({ projectHasErc20Token={projectHasErc20Token} isIssuingTokens={isIssuingTokens} /> - ) : null - // - } + ) : ( + + )}
diff --git a/src/packages/v4/components/ReservedTokensList.tsx b/src/packages/v4/components/ReservedTokensList.tsx new file mode 100644 index 0000000000..b175d14ff0 --- /dev/null +++ b/src/packages/v4/components/ReservedTokensList.tsx @@ -0,0 +1,71 @@ +import { TrashIcon } from '@heroicons/react/24/outline' +import { t } from '@lingui/macro' +import { Button } from 'antd' +import { FormItemInput } from 'models/formItemInput' + +import { SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' +import { useMemo } from 'react' +import { totalSplitsPercent } from '../utils/v4Splits' +import { Allocation, AllocationSplit } from './Allocation/Allocation' +import { AllocationItemTitle } from './Allocation/components/AllocationItemTitle' +import { OwnerPayoutCard } from './PayoutCard/OwnerPayoutCard' + +export const ReservedTokensList: React.FC< + React.PropsWithChildren< + FormItemInput & { isEditable?: boolean } + > +> = ({ isEditable, value, onChange }) => { + const totalPercent = useMemo( + () => (value ? totalSplitsPercent(value) : 0n), + [value], + ) + + return ( + +
+ {totalPercent < BigInt(SPLITS_TOTAL_PERCENT) ? ( + + ) : null} + + {( + modal, + { allocations, removeAllocation, setSelectedAllocation }, + ) => ( + <> + {allocations.map(allocation => ( + } + amount={`${allocation.percent.formatPercentage()}%`} + extra={ + isEditable ? ( + + ) : undefined + } + onClick={() => { + if (!isEditable) return + setSelectedAllocation(allocation) + modal.open() + }} + /> + ))} + + )} + +
+
+ ) +} diff --git a/src/packages/v4/components/V4EditReservedTokens.tsx b/src/packages/v4/components/V4EditReservedTokens.tsx new file mode 100644 index 0000000000..f69d1faecf --- /dev/null +++ b/src/packages/v4/components/V4EditReservedTokens.tsx @@ -0,0 +1,58 @@ +import { Trans } from '@lingui/macro' +import { Callout } from 'components/Callout/Callout' +import { JBSplit as Split } from 'juice-sdk-core' +import { useCallback } from 'react' +import { allocationToSplit, splitToAllocation } from '../utils/splitToAllocation' +import { AllocationSplit } from './Allocation/Allocation' +import { ReservedTokensList } from './ReservedTokensList' + +export function V4EditReservedTokens({ + editingReservedTokensSplits, + setEditingReservedTokensSplits, + showInstantChangesCallout, + hideTitle, +}: { + editingReservedTokensSplits: Split[] + setEditingReservedTokensSplits: (splits: Split[]) => void + showInstantChangesCallout?: boolean + hideTitle?: boolean +}) { + const onSplitsChanged = useCallback( + (newSplits: Split[]) => { + setEditingReservedTokensSplits(newSplits) + }, + [setEditingReservedTokensSplits], + ) + + const onAllocationChanged = (newAllocations: AllocationSplit[]) => { + onSplitsChanged(newAllocations.map(allocationToSplit)) + } + + return ( + <> + {showInstantChangesCallout ? ( + + + Changes to your reserved token recipients will take effect + immediately. + + + ) : null} +
+ {/*
+ {hideTitle ?
: Reserved token recipients} + +
*/} + +
+ + ) +} diff --git a/src/packages/v4/graphql/queries/project.graphql b/src/packages/v4/graphql/queries/project.graphql index 8cecf55ed8..0c51032449 100644 --- a/src/packages/v4/graphql/queries/project.graphql +++ b/src/packages/v4/graphql/queries/project.graphql @@ -1,5 +1,5 @@ -query Projects($where: Project_filter, $first: Int, $skip: Int) { - projects(where: $where, first: $first, skip: $skip) { +query Projects($where: Project_filter, $first: Int, $skip: Int, $block: Block_height) { + projects(where: $where, first: $first, skip: $skip, block: $block) { projectId metadata handle diff --git a/src/packages/v4/hooks/useCurrentRouteChainId.ts b/src/packages/v4/hooks/useCurrentRouteChainId.ts new file mode 100644 index 0000000000..58db1b737a --- /dev/null +++ b/src/packages/v4/hooks/useCurrentRouteChainId.ts @@ -0,0 +1,12 @@ +import { useRouter } from 'next/router' +import { chainNameMap } from '../utils/networks' + +export function useCurrentRouteChainId() { + const router = useRouter() + const { chainName } = router.query + if (!chainName) { + return null + } + + return chainNameMap[chainName as string] +} diff --git a/src/packages/v4/hooks/useETHReceivedFromTokens.ts b/src/packages/v4/hooks/useETHReceivedFromTokens.ts new file mode 100644 index 0000000000..0847ee6b22 --- /dev/null +++ b/src/packages/v4/hooks/useETHReceivedFromTokens.ts @@ -0,0 +1,43 @@ +import { getTokenRedemptionQuoteEth } from 'juice-sdk-core' +import { + useJBContractContext, + useJBRulesetContext, + useNativeTokenSurplus, + useReadJbControllerPendingReservedTokenBalanceOf, + useReadJbTokensTotalSupplyOf, +} from 'juice-sdk-react' + +export function useETHReceivedFromTokens( + tokenAmountWei: bigint | undefined, +): bigint | undefined { + const { projectId, contracts } = useJBContractContext() + const { rulesetMetadata } = useJBRulesetContext() + const { data: totalSupply } = useReadJbTokensTotalSupplyOf({ + args: [projectId], + }) + const { data: nativeTokenSurplus } = useNativeTokenSurplus() + + const { data: tokensReserved } = + useReadJbControllerPendingReservedTokenBalanceOf({ + address: contracts.controller.data ?? undefined, + args: [projectId], + }) + const redemptionRate = rulesetMetadata.data?.redemptionRate?.value + + if ( + !redemptionRate || + !totalSupply || + !tokensReserved || + !tokenAmountWei || + !nativeTokenSurplus + ) { + return + } + + return getTokenRedemptionQuoteEth(tokenAmountWei, { + redemptionRate: Number(redemptionRate), + totalSupply, + tokensReserved, + overflowWei: nativeTokenSurplus, + }) +} diff --git a/src/packages/v4/hooks/usePayoutLimit.ts b/src/packages/v4/hooks/usePayoutLimit.ts index c1ab6c1abe..b01314a59a 100644 --- a/src/packages/v4/hooks/usePayoutLimit.ts +++ b/src/packages/v4/hooks/usePayoutLimit.ts @@ -1,7 +1,12 @@ -import * as constants from '@ethersproject/constants'; -import { NATIVE_TOKEN } from 'juice-sdk-core'; -import { useJBContractContext, useJBRuleset, useReadJbFundAccessLimitsPayoutLimitsOf } from 'juice-sdk-react'; -import { V4CurrencyOption } from '../models/v4CurrencyOption'; +import * as constants from '@ethersproject/constants' +import { NATIVE_TOKEN } from 'juice-sdk-core' +import { + useJBContractContext, + useJBRuleset, + useReadJbFundAccessLimitsPayoutLimitsOf, +} from 'juice-sdk-react' +import { V4CurrencyOption } from '../models/v4CurrencyOption' +import { V4_CURRENCY_ETH } from '../utils/currency' /** * @todo add to sdk @@ -10,26 +15,31 @@ export function usePayoutLimit() { const { projectId, contracts: { primaryNativeTerminal, fundAccessLimits }, - } = useJBContractContext(); + } = useJBContractContext() + const { data: ruleset } = useJBRuleset() + const { data: payoutLimits, isLoading } = + useReadJbFundAccessLimitsPayoutLimitsOf({ + address: fundAccessLimits.data ?? undefined, + args: [ + projectId, + BigInt(ruleset?.id ?? 0), + primaryNativeTerminal.data ?? constants.AddressZero, + NATIVE_TOKEN, + ], + }) - const { data: ruleset } = useJBRuleset(); + const payoutLimit = payoutLimits?.[0] - - const { data: payoutLimits, isLoading } = useReadJbFundAccessLimitsPayoutLimitsOf({ - address: fundAccessLimits.data ?? undefined, - args: [ - projectId, - BigInt(ruleset?.id ?? 0), - primaryNativeTerminal.data ?? constants.AddressZero, - NATIVE_TOKEN, - ], - }); - const payoutLimit = payoutLimits?.[0]; return { - data: payoutLimit ? { - ...payoutLimit, - currency: Number(payoutLimit.currency) as V4CurrencyOption, - }: undefined, - isLoading - }; + data: payoutLimit + ? { + ...payoutLimit, + currency: Number(payoutLimit.currency) as V4CurrencyOption, + } + : { + amount: 0n, + currency: V4_CURRENCY_ETH, + }, + isLoading, + } } diff --git a/src/packages/v4/utils/fundingCycle.ts b/src/packages/v4/utils/fundingCycle.ts index 52c8e8e887..c501963098 100644 --- a/src/packages/v4/utils/fundingCycle.ts +++ b/src/packages/v4/utils/fundingCycle.ts @@ -1,5 +1,8 @@ import { MAX_PAYOUT_LIMIT } from "./math" +export const WEIGHT_ZERO = 1 // send `1` when we want to set the weight to `0` +export const WEIGHT_UNCHANGED = 0 // send `0` when we don't want to change the weight. + export function isInfinitePayoutLimit( payoutLimit: bigint | undefined, ) { diff --git a/src/packages/v4/utils/v4Splits.ts b/src/packages/v4/utils/v4Splits.ts index 9e4f413ee6..ae49175246 100644 --- a/src/packages/v4/utils/v4Splits.ts +++ b/src/packages/v4/utils/v4Splits.ts @@ -44,7 +44,7 @@ export const totalSplitsPercent = (splits: JBSplit[]): bigint => // - false if new (exists in new but not old) // - JBSplit if exists in old and new and there is a diff in the splits // - undefined if exists in old and new and there is no diff in the splits -type OldSplit = JBSplit | boolean | undefined +type OldSplit = (JBSplit & { totalValue?: bigint } ) | boolean | undefined export type SplitWithDiff = JBSplit & { oldSplit?: OldSplit @@ -99,7 +99,7 @@ export const sortSplits = (splits: JBSplit[]) => { } /* Determines if two splits AMOUNTS are equal. Extracts amounts for two splits from their respective totalValues **/ -function splitAmountsAreEqual({ +export function splitAmountsAreEqual({ split1, split2, split1TotalValue, diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/activityEventElems/ActivityElement.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/activityEventElems/ActivityElement.tsx index 4ecc0d079b..420907d550 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/activityEventElems/ActivityElement.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/activityEventElems/ActivityElement.tsx @@ -45,7 +45,7 @@ const FromBeneficiary = ({ {/* {distributionFromProjectId && (
Paid from:{' '} - @@ -56,7 +56,7 @@ const FromBeneficiary = ({ >
{/* {distributionFromProjectId ? ( - - {pv === PV_V2 ? ( - - ) : ( - - )} + )} */}
diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx index 048931eed8..012fd2e815 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx @@ -1,17 +1,26 @@ import { t, Trans } from '@lingui/macro' +import { waitForTransactionReceipt } from '@wagmi/core' import { Form } from 'antd' import InputAccessoryButton from 'components/buttons/InputAccessoryButton' import { Callout } from 'components/Callout/Callout' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' import TransactionModal from 'components/modals/TransactionModal' import { FEES_EXPLANATION } from 'components/strings' +import { useProjectMetadataContext } from 'contexts/ProjectMetadataContext' +import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext' +import { NATIVE_TOKEN, NATIVE_TOKEN_DECIMALS } from 'juice-sdk-core' +import { + useJBContractContext, + useWriteJbMultiTerminalSendPayoutsOf, +} from 'juice-sdk-react' import { PayoutsTable } from 'packages/v4/components/PayoutsTable/PayoutsTable' import { usePayoutLimit } from 'packages/v4/hooks/usePayoutLimit' -import { useUsedPayoutLimitOf } from 'packages/v4/hooks/useUsedPayoutLimitOf' -import { useV4BalanceOfNativeTerminal } from 'packages/v4/hooks/useV4BalanceOfNativeTerminal' import { useV4CurrentPayoutSplits } from 'packages/v4/hooks/useV4PayoutSplits' -import { V4CurrencyName } from 'packages/v4/utils/currency' -import { useEffect, useState } from 'react' +import { V4_CURRENCY_ETH, V4CurrencyName } from 'packages/v4/utils/currency' +import { wagmiConfig } from 'packages/v4/wagmiConfig' +import { useContext, useState } from 'react' +import { emitErrorNotification } from 'utils/notifications' +import { parseUnits } from 'viem' import { useV4DistributableAmount } from './hooks/useV4DistributableAmount' export default function V4DistributePayoutsModal({ @@ -24,52 +33,59 @@ export default function V4DistributePayoutsModal({ onConfirmed?: VoidFunction }) { const { splits: payoutSplits } = useV4CurrentPayoutSplits() - const { data: usedPayoutLimit } = useUsedPayoutLimitOf() const { data: payoutLimit } = usePayoutLimit() - const { data: balanceOfNativeTerminal } = useV4BalanceOfNativeTerminal() const { distributableAmount: distributable } = useV4DistributableAmount() + const { projectId } = useProjectMetadataContext() + const { contracts } = useJBContractContext() + const { addTransaction } = useContext(TxHistoryContext) - const payoutLimitAmount = payoutLimit?.amount - const payoutLimitAmountCurrency = payoutLimit?.currency + const payoutLimitAmountCurrency = payoutLimit?.currency ?? V4_CURRENCY_ETH const [transactionPending, setTransactionPending] = useState() const [loading, setLoading] = useState() const [distributionAmount, setDistributionAmount] = useState() - // TODO: const v4DistributePayoutsTx = useV4DistributePayoutsTx() - - useEffect(() => { - setDistributionAmount(distributable.format()) - }, [ - distributable, - ]) + const { writeContractAsync: writeSendPayouts, data } = + useWriteJbMultiTerminalSendPayoutsOf() async function executeDistributePayoutsTx() { - if (!payoutLimitAmountCurrency || !distributionAmount) return + if ( + !payoutLimitAmountCurrency || + !distributionAmount || + !contracts.primaryNativeTerminal.data || + !projectId + ) + return setLoading(true) - const txSuccessful = true - // TODO: const txSuccessful = await v4DistributePayoutsTx( - // { - // amount: distributionAmount, - // currency: payoutLimitAmountCurrency, - // }, - // { - // onDone: () => { - // setTransactionPending(true) - // }, - // onConfirmed: () => { - // setLoading(false) - // setTransactionPending(false) - // onConfirmed?.() - // }, - // }, - // ) + const args = [ + BigInt(projectId), + NATIVE_TOKEN, + parseUnits(distributionAmount, NATIVE_TOKEN_DECIMALS), + BigInt(payoutLimitAmountCurrency), + 0n, // TODO? + ] as const + + try { + const hash = await writeSendPayouts({ + address: contracts.primaryNativeTerminal.data, + args, + }) + setTransactionPending(true) + + addTransaction?.('Send payouts', { hash }) + await waitForTransactionReceipt(wagmiConfig, { + hash, + }) - if (!txSuccessful) { setLoading(false) setTransactionPending(false) + onConfirmed?.() + } catch (e) { + setLoading(false) + + emitErrorNotification((e as unknown as Error).message) } } diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4PayoutsSubPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4PayoutsSubPanel.tsx index e1e3331f43..8f70cbdc6c 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4PayoutsSubPanel.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4PayoutsSubPanel.tsx @@ -1,6 +1,5 @@ import { Trans, t } from '@lingui/macro' import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard' -import { useMemo } from 'react' import { twMerge } from 'tailwind-merge' import { useV4PayoutsSubPanel } from './hooks/useV4PayoutsSubPanel' import { V4ExportPayoutsCsvItem } from './V4ExportPayoutsCsvItem' @@ -18,11 +17,9 @@ export const V4PayoutsSubPanel = ({ const { payouts, isLoading, totalPayoutAmount, payoutLimit } = useV4PayoutsSubPanel(type) - const hasPayouts = useMemo(() => { - if (!payouts || payouts.length === 0 || payoutLimit === 0n) - return false - return true - }, [payouts, payoutLimit]) + const hasPayouts = + !payouts || payouts.length === 0 || payoutLimit === 0n ? false : true + return (

diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4DistributableAmount.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4DistributableAmount.ts index 5929caacc7..b43dda1ce3 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4DistributableAmount.ts +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4DistributableAmount.ts @@ -6,24 +6,20 @@ import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math' export const useV4DistributableAmount = () => { const { data: usedPayoutLimit } = useUsedPayoutLimitOf() - const { data: _treasuryBalance } = useV4BalanceOfNativeTerminal() - const { data: payoutLimit } = usePayoutLimit() const effectiveDistributionLimit = payoutLimit?.amount ?? MAX_PAYOUT_LIMIT const distributedAmount = usedPayoutLimit ?? 0n - const treasuryBalance = - _treasuryBalance ?? 0n + const treasuryBalance = _treasuryBalance ?? 0n const distributable = effectiveDistributionLimit === 0n ? effectiveDistributionLimit : effectiveDistributionLimit - distributedAmount - const distributableAmount = treasuryBalance > distributable - ? distributable - : treasuryBalance + const distributableAmount = + treasuryBalance > distributable ? distributable : treasuryBalance return { distributableAmount: new JBProjectToken(distributableAmount), diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4PayoutsSubPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4PayoutsSubPanel.tsx index dced7b6d74..b706b591ae 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4PayoutsSubPanel.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4PayoutsSubPanel.tsx @@ -30,19 +30,14 @@ const calculateSplitAmountWad = ( export const useV4PayoutsSubPanel = (type: 'current' | 'upcoming') => { const { splits, isLoading } = useV4CurrentUpcomingPayoutSplits(type) - const { data: projectOwnerAddress } = useProjectOwnerOf() - const { data: primaryNativeTerminalFee } = useReadJbMultiTerminalFee() - const { distributableAmount } = useV4DistributableAmount() const { payoutLimit, payoutLimitCurrency } = useV4CurrentUpcomingPayoutLimit(type) - - const showAmountOnPayout = useMemo(() => { - return payoutLimit === MAX_PAYOUT_LIMIT || payoutLimit === 0n - }, [payoutLimit]) + const showAmountOnPayout = + payoutLimit !== MAX_PAYOUT_LIMIT && payoutLimit !== 0n const transformSplit = useCallback( (split: JBSplit) => { diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4DistributeReservedTokensModal.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4DistributeReservedTokensModal.tsx index 6e48caa12a..981da7cab9 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4DistributeReservedTokensModal.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4DistributeReservedTokensModal.tsx @@ -1,13 +1,13 @@ import { t, Trans } from '@lingui/macro' import TransactionModal from 'components/modals/TransactionModal' import useNameOfERC20 from 'hooks/ERC20/useNameOfERC20' -import { useJBContractContext, useReadJbTokensTokenOf, useReadJbTokensTotalCreditSupplyOf } from 'juice-sdk-react' +import { useJBContractContext, useReadJbTokensTokenOf } from 'juice-sdk-react' import SplitList from 'packages/v4/components/SplitList/SplitList' import useProjectOwnerOf from 'packages/v4/hooks/useV4ProjectOwnerOf' import { useV4ReservedSplits } from 'packages/v4/hooks/useV4ReservedSplits' import { useState } from 'react' -import { formatWad } from 'utils/format/formatNumber' import { tokenSymbolText } from 'utils/tokenSymbolText' +import { useV4ReservedTokensSubPanel } from './hooks/useV4ReservedTokensSubPanel' export default function V4DistributeReservedTokensModal({ open, @@ -24,14 +24,13 @@ export default function V4DistributeReservedTokensModal({ const { data: tokenAddress } = useReadJbTokensTokenOf() const { data: tokenSymbol } = useNameOfERC20(tokenAddress) - const [loading, setLoading] = useState() const [transactionPending, setTransactionPending] = useState() // const distributeReservedTokensTx = useDistributeReservedTokens() - const { data: totalCreditSupply } = useReadJbTokensTotalCreditSupplyOf({ - args: [projectId], - }) + const { pendingReservedTokens, pendingReservedTokensFormatted } = + useV4ReservedTokensSubPanel() + async function distributeReservedTokens() { setLoading(true) @@ -50,13 +49,11 @@ export default function V4DistributeReservedTokensModal({ // ) // if (!txSuccessful) { - setLoading(false) - setTransactionPending(false) + setLoading(false) + setTransactionPending(false) // } } - const reservedTokensFormatted = formatWad(totalCreditSupply, { precision: 0 }) - const tokenTextPlural = tokenSymbolText({ tokenSymbol, capitalize: false, @@ -79,7 +76,6 @@ export default function V4DistributeReservedTokensModal({ confirmLoading={loading} transactionPending={transactionPending} onCancel={onCancel} - okButtonProps={{ disabled: !(totalCreditSupply && totalCreditSupply > 0n) }} width={640} centered={true} > @@ -88,7 +84,7 @@ export default function V4DistributeReservedTokensModal({ Reserved {tokenTextPlural}:{' '} - {reservedTokensFormatted} {tokenTextPlural} + {pendingReservedTokensFormatted} {tokenTextPlural}

@@ -109,7 +105,7 @@ export default function V4DistributeReservedTokensModal({ { - const { reservedList, totalCreditSupply, reservedPercent } = + const { reservedList, pendingReservedTokensFormatted, reservedPercent } = useV4ReservedTokensSubPanel() const reservedPercentTooltip = ( @@ -32,8 +32,8 @@ export const V4ReservedTokensSubPanel = ({ className="w-full min-w-min flex-[1_0_0]" title={t`Reserved tokens`} description={ - totalCreditSupply ? ( - <>{totalCreditSupply} + pendingReservedTokensFormatted ? ( + <>{pendingReservedTokensFormatted} ) : (
) @@ -48,7 +48,7 @@ export const V4ReservedTokensSubPanel = ({ />
{reservedPercent && - totalCreditSupply && + pendingReservedTokensFormatted && reservedPercent !== '0' ? ( - {totalCreditSupply || + {pendingReservedTokensFormatted || reservedPercent || (reservedList && reservedList.length > 1) ? ( <> diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4SendReservedTokensButton.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4SendReservedTokensButton.tsx index 879843185c..49c9aa9f50 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4SendReservedTokensButton.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4SendReservedTokensButton.tsx @@ -1,11 +1,11 @@ import { ArrowUpCircleIcon } from '@heroicons/react/24/outline' import { Trans } from '@lingui/macro' import { Button, Tooltip } from 'antd' -import { useJBContractContext, useReadJbTokensTotalCreditSupplyOf } from 'juice-sdk-react' -import { useCallback, useMemo, useState } from 'react' +import { useCallback, useState } from 'react' import { twMerge } from 'tailwind-merge' import { reloadWindow } from 'utils/windowUtils' import V4DistributeReservedTokensModal from './V4DistributeReservedTokensModal' +import { useV4ReservedTokensSubPanel } from './hooks/useV4ReservedTokensSubPanel' export const V4SendReservedTokensButton = ({ className, @@ -14,17 +14,14 @@ export const V4SendReservedTokensButton = ({ className?: string containerClassName?: string }) => { - const { projectId } = useJBContractContext() - const { data: totalCreditSupply, isLoading: totalCreditSupplyLoading } = useReadJbTokensTotalCreditSupplyOf({ - args: [projectId], - }) + const { pendingReservedTokens } = useV4ReservedTokensSubPanel() + const [open, setOpen] = useState(false) const openModal = useCallback(() => setOpen(true), []) const closeModal = useCallback(() => setOpen(false), []) - const distributeButtonDisabled = useMemo(() => { - return (totalCreditSupply ?? 0n) === 0n - }, [totalCreditSupply]) + const distributeButtonDisabled = + !pendingReservedTokens || pendingReservedTokens === 0n return ( diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4ReservedTokensSubPanel.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4ReservedTokensSubPanel.ts index a1c1787d52..dc02b87b25 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4ReservedTokensSubPanel.ts +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4ReservedTokensSubPanel.ts @@ -1,26 +1,26 @@ -import { WeiPerEther } from '@ethersproject/constants' -import { SplitPortion, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' +import { formatEther, SplitPortion, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' import { useJBContractContext, useJBRulesetMetadata, - useReadJbTokensTotalCreditSupplyOf, + useReadJbControllerPendingReservedTokenBalanceOf, } from 'juice-sdk-react' import useProjectOwnerOf from 'packages/v4/hooks/useV4ProjectOwnerOf' import { useV4ReservedSplits } from 'packages/v4/hooks/useV4ReservedSplits' import { useMemo } from 'react' import assert from 'utils/assert' -import { formatAmount } from 'utils/format/formatAmount' export const useV4ReservedTokensSubPanel = () => { - const { projectId } = useJBContractContext() + const { projectId, contracts } = useJBContractContext() const { data: projectOwnerAddress } = useProjectOwnerOf() const { splits: reservedTokensSplits } = useV4ReservedSplits() const { data: rulesetMetadata } = useJBRulesetMetadata() const reservedPercent = `${rulesetMetadata?.reservedPercent.formatPercentage()}%` - const { data: totalCreditSupply } = useReadJbTokensTotalCreditSupplyOf({ - args: [projectId], - }) + const { data: pendingReservedTokens } = + useReadJbControllerPendingReservedTokenBalanceOf({ + address: contracts.controller.data ?? undefined, + args: [projectId], + }) const reservedList = useMemo(() => { if (!projectOwnerAddress || !projectId || !reservedTokensSplits) return @@ -78,16 +78,15 @@ export const useV4ReservedTokensSubPanel = () => { return processedSplits }, [reservedTokensSplits, projectOwnerAddress, projectId]) - const totalCreditSupplyFormatted = useMemo(() => { - if (totalCreditSupply === undefined) return - return formatAmount(Number(totalCreditSupply / WeiPerEther.toBigInt()), { - maximumFractionDigits: 2, - }) - }, [totalCreditSupply]) + const pendingReservedTokensFormatted = useMemo(() => { + if (pendingReservedTokens === undefined) return + return formatEther(pendingReservedTokens, { fractionDigits: 6 }) + }, [pendingReservedTokens]) return { reservedList, - totalCreditSupply: totalCreditSupplyFormatted, + pendingReservedTokensFormatted: pendingReservedTokensFormatted, + pendingReservedTokens: pendingReservedTokens, reservedPercent, } } diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/DetailsSection/CycleDeadlineDropdown.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/DetailsSection/CycleDeadlineDropdown.tsx index 20485d30c5..de0fe54563 100644 --- a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/DetailsSection/CycleDeadlineDropdown.tsx +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/DetailsSection/CycleDeadlineDropdown.tsx @@ -7,16 +7,17 @@ export default function CycleDeadlineDropdown({ }: { className?: string }) { - // const { cv } = useContext(V2V3ContractsContext) // const ballotStrategies = ballotStrategiesFn({ cv }) return ( ) diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/EditCyclePage.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/EditCyclePage.tsx index 806b9aa854..1e5880b8c9 100644 --- a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/EditCyclePage.tsx +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/EditCyclePage.tsx @@ -13,7 +13,8 @@ import { useChainId } from 'wagmi' import { DetailsSection } from './DetailsSection' import { useEditCycleFormContext } from './EditCycleFormContext' import { PayoutsSection } from './PayoutsSection' -// import { DetailsSection } from './DetailsSection' +import { ReviewConfirmModal } from './ReviewConfirmModal' +import { TokensSection } from './TokensSection' export function EditCyclePage() { const [confirmModalOpen, setConfirmModalOpen] = useState(false) @@ -106,7 +107,7 @@ export function EditCyclePage() { - {/* Tokens} description={ @@ -119,7 +120,7 @@ export function EditCyclePage() { setConfirmModalOpen(false)} - /> */} + />
@@ -132,13 +133,13 @@ export function EditCyclePage() { ) : null} {/* */} - + {/* */}
diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DetailsSectionDiff.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DetailsSectionDiff.tsx new file mode 100644 index 0000000000..8634115038 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DetailsSectionDiff.tsx @@ -0,0 +1,145 @@ +import { Trans, t } from '@lingui/macro' + +import { FundingCycleListItem } from 'components/FundingCycleListItem' +import { DiffSection } from './DiffSection' +import { ApprovalStrategyValue } from './FormattedRulesetValues/DetailsSection/ApprovalStrategyValue' +import { DurationValue } from './FormattedRulesetValues/DetailsSection/DurationValue' +import { useDetailsSectionValues } from './hooks/useDetailsSectionValues' + +export const emptySectionClasses = 'text-sm text-secondary pt-2 pb-3' + +export function DetailsSectionDiff() { + const { + advancedOptionsHasDiff, + sectionHasDiff, + + currentDuration, + newDuration, + durationHasDiff, + + currentBallot, + newBallot, + ballotHasDiff, + + newPausePay, + currentPausePay, + pausePayHasDiff, + + newSetTerminals, + currentSetTerminals, + allowSetTerminalsHasDiff, + + newAllowTerminalMigration, + currentAllowTerminalMigration, + allowTerminalMigrationHasDiff, + + newSetController, + currentSetController, + allowSetControllerHasDiff, + } = useDetailsSectionValues() + + if (!sectionHasDiff) { + return ( +
+ No edits were made to cycle details for this cycle. +
+ ) + } + + return ( + + {durationHasDiff && ( + } + oldValue={} + /> + )} + {ballotHasDiff && currentBallot && ( + + } + oldValue={ + + } + /> + )} + + } + advancedOptions={ + advancedOptionsHasDiff && ( + <> + {pausePayHasDiff && ( + {newPausePay.toString()} + } + oldValue={ + + {currentPausePay.toString()} + + } + /> + )} + {allowSetTerminalsHasDiff && ( + + {newSetTerminals.toString()} + + } + oldValue={ + + {currentSetTerminals.toString()} + + } + /> + )} + {allowTerminalMigrationHasDiff && ( + + {newAllowTerminalMigration.toString()} + + } + oldValue={ + + {currentAllowTerminalMigration.toString()} + + } + /> + )} + {allowSetControllerHasDiff && ( + + {newSetController.toString()} + + } + oldValue={ + + {currentSetController.toString()} + + } + /> + )} + + ) + } + /> + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffSection.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffSection.tsx new file mode 100644 index 0000000000..69f08c39aa --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffSection.tsx @@ -0,0 +1,23 @@ +import { Trans } from '@lingui/macro' + +export function DiffSection({ + content, + advancedOptions, +}: { + content: React.ReactNode + advancedOptions?: React.ReactNode +}) { + return ( +
+ {content} + {advancedOptions ? ( + <> +
+ Advanced options: +
+ {advancedOptions} + + ) : null} +
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedJBProjectBeneficiary.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedJBProjectBeneficiary.tsx new file mode 100644 index 0000000000..404811f91c --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedJBProjectBeneficiary.tsx @@ -0,0 +1,34 @@ +import { DiffedItem } from 'components/DiffedItem' +import EthereumAddress from 'components/EthereumAddress' +import { JBSplit } from 'juice-sdk-core' +import { JuiceboxProjectBeneficiary } from 'packages/v4/components/SplitList/SplitItem/JuiceboxProjectBeneficiary' + +export function DiffedJBProjectBeneficiary({ + split, + oldSplit, +}: { + split: JBSplit + oldSplit?: JBSplit +}) { + const hasDiff = oldSplit && oldSplit.beneficiary !== split.beneficiary + + return ( + + } + diffStatus="old" + /> + } + diffStatus="new" + /> + + ) : undefined + } + /> + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedLockedUntil.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedLockedUntil.tsx new file mode 100644 index 0000000000..85ee77a65b --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedLockedUntil.tsx @@ -0,0 +1,72 @@ +import { DiffedItem } from 'components/DiffedItem' +import { LockedUntilValue } from 'packages/v4/components/SplitList/SplitItem/LockedUntilValue' +import { formatDate } from 'utils/format/formatDate' + +export function DiffedLockedUntil({ + lockedUntil, + oldLockedUntil, +}: { + lockedUntil: number | undefined + oldLockedUntil?: number +}) { + const lockedUntilFormatted = lockedUntil + ? formatDate(lockedUntil * 1000, 'yyyy-MM-DD') + : undefined + const oldLockedUntilFormatted = oldLockedUntil + ? formatDate(oldLockedUntil * 1000, 'yyyy-MM-DD') + : undefined + + const hasOldLockedUntil = + oldLockedUntilFormatted && oldLockedUntil && oldLockedUntil > 0 + const hasNewLockedUntil = lockedUntil && lockedUntil > 0 + const hasDiff = lockedUntil !== oldLockedUntil + + if (!hasNewLockedUntil && !hasOldLockedUntil) return null + + if (hasDiff && !hasOldLockedUntil) { + // whole lockedUntil section green + return ( +
+ } + diffStatus="new" + /> +
+ ) + } + + if (hasDiff && hasOldLockedUntil) { + if (!hasNewLockedUntil) { + // whole lockedUntil section red + return ( +
+ } + diffStatus="old" + /> +
+ ) + } + + return ( + + {/* Diff within the lockedUntil section */} + {hasDiff && lockedUntilFormatted ? ( + <> + + + + ) : ( + // lockedUntil no diff + lockedUntilFormatted + )} + + } + /> + ) + } + + return +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedSplitValue.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedSplitValue.tsx new file mode 100644 index 0000000000..0fdc905254 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitFields/DiffedSplitValue.tsx @@ -0,0 +1,70 @@ +import { DiffedItem } from 'components/DiffedItem' +import { JBSplit } from 'juice-sdk-core' + +import { SplitProps } from 'packages/v4/components/SplitList/SplitItem' +import { SplitAmountValue } from 'packages/v4/components/SplitList/SplitItem/SplitAmountValue' +import { isFinitePayoutLimit, isInfinitePayoutLimit } from 'packages/v4/utils/fundingCycle' +import { splitAmountsAreEqual } from 'packages/v4/utils/v4Splits' + +export function DiffedSplitValue({ + splitProps, + diffSplit, +}: { + splitProps: SplitProps + diffSplit?: JBSplit & { totalValue?: bigint } +}) { + const diffSplitProps: SplitProps | undefined = diffSplit + ? { + split: diffSplit, + totalValue: diffSplit.totalValue, + projectOwnerAddress: splitProps.projectOwnerAddress, + currency: splitProps.currency, + } + : undefined + + const newValue = isInfinitePayoutLimit(splitProps?.totalValue) ? ( + <>{splitProps.split.percent.formatPercentage()}% + ) : ( + + ) + + if (!diffSplitProps) return newValue + + const oldValue = + diffSplit && isInfinitePayoutLimit(diffSplit?.totalValue) ? ( + <>{diffSplit.percent.formatPercentage()}% + ) : ( + + ) + + const isFiniteTotalValue = + isFinitePayoutLimit(splitProps.totalValue) && + isFinitePayoutLimit(diffSplitProps.totalValue) + + const percentsEqual = + splitProps.split.percent === diffSplitProps.split.percent + + const valuesEqual = isFiniteTotalValue + ? splitAmountsAreEqual({ + split1: splitProps.split, + split2: diffSplitProps.split, + split1TotalValue: splitProps.totalValue ?? 0n, + split2TotalValue: diffSplitProps.totalValue ?? 0n, + }) + : percentsEqual + + if (valuesEqual) return
{newValue}
+ + return ( +
+ + +
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitItem.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitItem.tsx new file mode 100644 index 0000000000..a0096a0422 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitItem.tsx @@ -0,0 +1,75 @@ +import { + DIFF_NEW_BACKGROUND, + DIFF_OLD_BACKGROUND, + DiffMinus, + DiffPlus, +} from 'components/DiffedItem' +import { SplitProps } from 'packages/v4/components/SplitList/SplitItem' +import { ETHAddressBeneficiary } from 'packages/v4/components/SplitList/SplitItem/EthAddressBeneficiary' +import { isJuiceboxProjectSplit, SplitWithDiff } from 'packages/v4/utils/v4Splits' +import { twMerge } from 'tailwind-merge' +import { DiffedJBProjectBeneficiary } from './DiffedSplitFields/DiffedJBProjectBeneficiary' +import { DiffedLockedUntil } from './DiffedSplitFields/DiffedLockedUntil' +import { DiffedSplitValue } from './DiffedSplitFields/DiffedSplitValue' + +type DiffedSplitProps = Omit & { split: SplitWithDiff } + +export function DiffedSplitItem({ props }: { props: DiffedSplitProps }) { + const isJuiceboxProject = isJuiceboxProjectSplit(props.split) + + const oldSplit = props.split.oldSplit + const splitIsRemoved = oldSplit === true + const splitIsNew = oldSplit === false + + const hasDiff = oldSplit !== undefined && !(splitIsRemoved || splitIsNew) + + const className = twMerge( + 'flex flex-wrap items-center justify-between py-1 rounded-md', + splitIsRemoved ? DIFF_OLD_BACKGROUND : undefined, + splitIsNew ? DIFF_NEW_BACKGROUND : undefined, + splitIsRemoved || splitIsNew ? 'pl-3 pr-4 my-1' : undefined, + ) + + return ( +
+
+
+ {splitIsRemoved ? : null} + {splitIsNew ? : null} + {isJuiceboxProject ? ( + + ) : ( + + )} +
+ + {(props.split.lockedUntil && props.split.lockedUntil > 0) || hasDiff ? ( + + ) : null} +
+
+ + {/* '?' icon that appears next reserved percents */} + {/* {props.reservedPercent !== undefined ? ( + + ) : null} */} +
+
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitList.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitList.tsx new file mode 100644 index 0000000000..dd18f15c57 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/DiffedSplits/DiffedSplitList.tsx @@ -0,0 +1,115 @@ +import { JBSplit as Split } from 'juice-sdk-core' +import round from 'lodash/round' + +import { SplitProps } from 'packages/v4/components/SplitList/SplitItem' +import useProjectOwnerOf from 'packages/v4/hooks/useV4ProjectOwnerOf' +import { processUniqueSplits, v4GetProjectOwnerRemainderSplit } from 'packages/v4/utils/v4Splits' +import { useMemo } from 'react' +import { DiffedSplitItem } from './DiffedSplitItem' + +const JB_PERCENT_PRECISION = 2 + +type DiffedSplitListProps = { + splits: Split[] + diffSplits?: Split[] + currency?: bigint + oldCurrency?: bigint + totalValue: bigint | undefined + oldTotalValue?: bigint + previousTotalValue?: bigint + showFees?: boolean + valueSuffix?: string | JSX.Element + valueFormatProps?: { precision?: number } + reservedPercent?: number + showDiffs?: boolean +} + +export default function DiffedSplitList({ + splits, + diffSplits, + showFees = false, + currency, + oldCurrency, + totalValue, + previousTotalValue, + valueSuffix, + valueFormatProps, + reservedPercent, + showDiffs, +}: DiffedSplitListProps) { + const { data: projectOwnerAddress } = useProjectOwnerOf() + const ownerSplit = useMemo(() => { + if (!projectOwnerAddress) return + return v4GetProjectOwnerRemainderSplit(projectOwnerAddress, splits) + }, [projectOwnerAddress, splits]) + + const diffOwnerSplit = useMemo(() => { + if (!diffSplits || !projectOwnerAddress || !showDiffs) return + return v4GetProjectOwnerRemainderSplit(projectOwnerAddress, diffSplits) + }, [projectOwnerAddress, diffSplits, showDiffs]) + + const ownerSplitIsRemoved = + !ownerSplit?.percent && diffOwnerSplit?.percent.value === 0n + + const roundedDiffOwnerSplitPercent = round((diffOwnerSplit?.percent.formatPercentage() || 0), + JB_PERCENT_PRECISION, + ) + const diffOwnerSplitHasPercent = + diffOwnerSplit && roundedDiffOwnerSplitPercent !== 0 + + const ownerSplitIsNew = ownerSplit?.percent && !diffOwnerSplitHasPercent + + const currencyHasDiff = Boolean( + oldCurrency && currency && (oldCurrency !== currency), + ) + const uniqueSplits = processUniqueSplits({ + oldTotalValue: previousTotalValue, + newTotalValue: totalValue, + allSplitsChanged: currencyHasDiff, + oldSplits: showDiffs ? diffSplits : undefined, + newSplits: splits, + }) + + const splitProps: Omit = { + currency, + oldCurrency, + totalValue, + projectOwnerAddress, + valueSuffix, + valueFormatProps, + reservedPercent, + showFee: showFees, + } + return ( +
+ {uniqueSplits.map(split => { + const splitIsRemoved = split.oldSplit === true + return ( + + ) + })} + {ownerSplit?.percent ? ( + + ) : null} +
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/AllowedValue.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/AllowedValue.tsx new file mode 100644 index 0000000000..1da80dfa51 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/AllowedValue.tsx @@ -0,0 +1,5 @@ +import { formatAllowed } from 'utils/format/formatBoolean' + +export function AllowedValue({ value }: { value: boolean | undefined }) { + return <>{formatAllowed(value)} +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/ApprovalStrategyValue.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/ApprovalStrategyValue.tsx new file mode 100644 index 0000000000..1065300b86 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/ApprovalStrategyValue.tsx @@ -0,0 +1,27 @@ +import { Tooltip } from 'antd' +import EtherscanLink from 'components/EtherscanLink' +import FundingCycleDetailWarning from 'components/Project/FundingCycleDetailWarning' +import { BallotStrategy } from 'models/ballot' + +export function ApprovalStrategyValue({ + approvalStrategy, + warningText, +}: { + approvalStrategy: BallotStrategy + warningText: string | undefined +}) { + return ( + + } + > + + {approvalStrategy.name} + + + + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/DurationValue.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/DurationValue.tsx new file mode 100644 index 0000000000..51b0c10917 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/DurationValue.tsx @@ -0,0 +1,32 @@ +import { Trans } from '@lingui/macro' +import FundingCycleDetailWarning from 'components/Project/FundingCycleDetailWarning' +import { FUNDING_CYCLE_WARNING_TEXT } from 'constants/fundingWarningText' +import { detailedTimeString } from 'utils/format/formatTime' + +export function DurationValue({ + duration, +}: { + duration: number | undefined +}) { + const formattedDuration = duration + ? detailedTimeString({ + timeSeconds: duration, + fullWords: true, + }) + : undefined + const riskWarningText = FUNDING_CYCLE_WARNING_TEXT() + return ( + <> + {duration && duration > 0 ? ( + formattedDuration + ) : ( + + Not set + + )} + + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/PayoutLimitValue.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/PayoutLimitValue.tsx new file mode 100644 index 0000000000..06edeb890c --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/DetailsSection/PayoutLimitValue.tsx @@ -0,0 +1,70 @@ +import { Trans } from '@lingui/macro' +import TooltipIcon from 'components/TooltipIcon' +import { CurrencyName } from 'constants/currency' +import { NativeTokenValue } from 'juice-sdk-react' +import { isInfinitePayoutLimit } from 'packages/v4/utils/fundingCycle' + +export function PayoutLimitValue({ + payoutLimit, + currencyName, + shortName, +}: { + payoutLimit: bigint | undefined + currencyName: CurrencyName | undefined + shortName?: boolean +}) { + const distributionLimitIsInfinite = + !payoutLimit || isInfinitePayoutLimit(payoutLimit) + const distributionLimitIsZero = payoutLimit === 0n + // const distributionLimitCurrency = currencyName + // ? getV4CurrencyOption(currencyName) + // : undefined + + const _tooltip = ( + + All of this project's ETH will be paid out. Token holders will + receive no ETH when redeeming their tokens. + + ) : distributionLimitIsZero ? ( + + No ETH can be paid out from the project. The ETH can only be + accessed by token holders that redeem their tokens. + + ) : ( + + If you don't raise this amount, your payout recipients will receive + their percentage of the available ETH. + + ) + } + placement={'topLeft'} + iconClassName="ml-1" + /> + ) + + const _text = distributionLimitIsInfinite ? ( + <> + {shortName ? ( + No limit + ) : ( + No limit (all available ETH) + )} + + ) : distributionLimitIsZero ? ( + <>{shortName ? Zero : Zero (no payouts)} + ) : ( + + ) + + return ( + + {_text} + {_tooltip} + + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/Tokens/IssuanceRateValue.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/Tokens/IssuanceRateValue.tsx new file mode 100644 index 0000000000..ac262b2222 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/FormattedRulesetValues/Tokens/IssuanceRateValue.tsx @@ -0,0 +1,22 @@ +import { Trans } from '@lingui/macro' +import { WEIGHT_UNCHANGED } from 'packages/v4/utils/fundingCycle' + +export function IssuanceRateValue({ + value, + tokenSymbol, + zeroAsUnchanged, +}: { + value: number + tokenSymbol: string + zeroAsUnchanged?: boolean +}) { + if (zeroAsUnchanged && value === WEIGHT_UNCHANGED) { + return Unchanged + } + + return ( + + {value} {tokenSymbol}/ETH + + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/PayoutsSectionDiff.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/PayoutsSectionDiff.tsx new file mode 100644 index 0000000000..f7b9c16d22 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/PayoutsSectionDiff.tsx @@ -0,0 +1,97 @@ +import { Trans, t } from '@lingui/macro' +import { FundingCycleListItem } from 'components/FundingCycleListItem' +import { getV4CurrencyOption } from 'packages/v4/utils/currency' +import { emptySectionClasses } from './DetailsSectionDiff' +import DiffedSplitList from './DiffedSplits/DiffedSplitList' +import { DiffSection } from './DiffSection' +import { PayoutLimitValue } from './FormattedRulesetValues/DetailsSection/PayoutLimitValue' +import { usePayoutsSectionValues } from './hooks/usePayoutsSectionValues' + +export function PayoutsSectionDiff() { + const { + sectionHasDiff, + advancedOptionsHasDiff, + + newCurrency, + currentCurrency, + + currentDistributionLimit, + newDistributionLimit, + distributionLimitHasDiff, + + currentPayoutSplits, + newPayoutSplits, + payoutSplitsHasDiff, + + newHoldFees, + currentHoldFees, + } = usePayoutsSectionValues() + + if (!sectionHasDiff) { + return ( +
+ No edits were made to payouts for this cycle. +
+ ) + } + + const roundingPrecision = newCurrency === 'ETH' ? 4 : 2 + + return ( + + {distributionLimitHasDiff && ( + + } + oldValue={ + + } + /> + )} + {payoutSplitsHasDiff && ( +
+
+ Payout recipients: +
+ +
+ )} + + } + advancedOptions={ + advancedOptionsHasDiff && ( + {newHoldFees.toString()}} + oldValue={ + {currentHoldFees.toString()} + } + /> + ) + } + /> + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx new file mode 100644 index 0000000000..980c8c784b --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx @@ -0,0 +1,146 @@ +import { Trans, t } from '@lingui/macro' +import { Form } from 'antd' +import { useWatch } from 'antd/lib/form/Form' +import { JuiceTextArea } from 'components/inputs/JuiceTextArea' +import TransactionModal from 'components/modals/TransactionModal' +import { CreateCollapse } from 'packages/v2v3/components/Create/components/CreateCollapse/CreateCollapse' +import { useState } from 'react' +// import { useReconfigureFundingCycle } from '../../../hooks/useReconfigureFundingCycle' +import { useEditCycleFormContext } from '../EditCycleFormContext' +// import { usePrepareSaveEditCycleData } from '../hooks/usePrepareSaveEditCycleData' +import { TransactionSuccessModal } from '../TransactionSuccessModal' +import { DetailsSectionDiff } from './DetailsSectionDiff' +import { PayoutsSectionDiff } from './PayoutsSectionDiff' +import { SectionCollapseHeader } from './SectionCollapseHeader' +import { TokensSectionDiff } from './TokensSectionDiff' +import { useDetailsSectionValues } from './hooks/useDetailsSectionValues' +import { usePayoutsSectionValues } from './hooks/usePayoutsSectionValues' +import { useTokensSectionValues } from './hooks/useTokensSectionValues' + +export function ReviewConfirmModal({ + open, + onClose, +}: { + open: boolean + onClose: VoidFunction +}) { + const [editCycleSuccessModalOpen, setEditCycleSuccessModalOpen] = + useState(false) + + const { editCycleForm } = useEditCycleFormContext() + + const { sectionHasDiff: detailsSectionHasDiff } = useDetailsSectionValues() + const { sectionHasDiff: payoutsSectionHasDiff } = usePayoutsSectionValues() + const { sectionHasDiff: tokensSectionHasDiff } = useTokensSectionValues() + + const formHasChanges = + detailsSectionHasDiff || payoutsSectionHasDiff || tokensSectionHasDiff + + const memo = useWatch('memo', editCycleForm) + // const { editingFundingCycleConfig } = usePrepareSaveEditCycleData() + + // const { reconfigureLoading, reconfigureFundingCycle } = + // useReconfigureFundingCycle({ + // editingFundingCycleConfig, + // memo: memo ?? '', + // onComplete: () => { + // editCycleForm?.resetFields() + // setEditCycleSuccessModalOpen(true) + // onClose() + // }, + // }) + + const panelProps = { className: 'text-lg' } + + return ( + <> + Review & confirm} + destroyOnClose + onOk={() => null}//reconfigureFundingCycle()} + okText={Deploy changes} + okButtonProps={{ disabled: !formHasChanges }} + cancelButtonProps={{ hidden: true }} + onCancel={onClose} + confirmLoading={false}//reconfigureLoading} + > +

+ + Please check your changes carefully. Each deploy will incur a gas + fee. + +

+ + Cycle details} + hasDiff={detailsSectionHasDiff} + /> + } + {...panelProps} + > + + + Payouts} + hasDiff={payoutsSectionHasDiff} + /> + } + {...panelProps} + > + + + Tokens} + hasDiff={tokensSectionHasDiff} + /> + } + {...panelProps} + > + + + +
+ + Memo (optional) + +
+ + + +
+ setEditCycleSuccessModalOpen(false)} + content={ + <> +
+ Your updated cycle has been deployed +
+
+ + Changes will take effect in your next cycle as long as it starts + after your edit deadline. + +
+ + } + /> + + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/SectionCollapseHeader.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/SectionCollapseHeader.tsx new file mode 100644 index 0000000000..c88ffb4612 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/SectionCollapseHeader.tsx @@ -0,0 +1,28 @@ +import { Trans } from '@lingui/macro' +import { Badge } from 'components/Badge' + +export function SectionCollapseHeader({ + title, + hasDiff, +}: { + title: React.ReactNode + hasDiff: boolean +}) { + const sectionHasEditsCard = ( + + Edited + + ) + + const sectionNoEditsCard = ( + + No changes + + ) + return ( +
+ {title} + {hasDiff ? sectionHasEditsCard : sectionNoEditsCard} +
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/TokensSectionDiff.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/TokensSectionDiff.tsx new file mode 100644 index 0000000000..56a3c83d22 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/TokensSectionDiff.tsx @@ -0,0 +1,144 @@ +import { Trans, t } from '@lingui/macro' +import { FundingCycleListItem } from 'components/FundingCycleListItem' +import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' +import { emptySectionClasses } from './DetailsSectionDiff' +import DiffedSplitList from './DiffedSplits/DiffedSplitList' +import { DiffSection } from './DiffSection' +import { AllowedValue } from './FormattedRulesetValues/AllowedValue' +import { IssuanceRateValue } from './FormattedRulesetValues/Tokens/IssuanceRateValue' +import { useTokensSectionValues } from './hooks/useTokensSectionValues' + +export function TokensSectionDiff() { + const { + sectionHasDiff, + + newMintRate, + mintRateHasDiff, + + newReservedRate, + currentReservedRate, + reservedRateHasDiff, + + newReservedSplits, + currentReservedSplits, + reservedSplitsHasDiff, + + newDiscountRate, + currentDiscountRate, + discountRateHasDiff, + + newRedemptionRate, + currentRedemptionRate, + redemptionHasDiff, + + newAllowMinting, + currentAllowMinting, + allowMintingHasDiff, + + newTokenTransfers, + currentTokenTransfers, + tokenTransfersHasDiff, + + // unsafeFundingCycleProperties, + tokenSymbolPlural, + } = useTokensSectionValues() + + const { + ruleset: upcomingRuleset, + } = useJBUpcomingRuleset() + + if (!sectionHasDiff) { + return ( +
+ No edits were made to tokens for this cycle. +
+ ) + } + + const currentMintRateAfterDiscountRateApplied = upcomingRuleset?.weight.toFloat() + + return ( + + {mintRateHasDiff && currentMintRateAfterDiscountRateApplied && ( + + } + oldValue={ + + } + /> + )} + {discountRateHasDiff && currentDiscountRate && ( + + )} + {redemptionHasDiff && currentRedemptionRate && ( + + )} + {allowMintingHasDiff && ( + + } + oldValue={ + + } + /> + )} + {tokenTransfersHasDiff && ( + + } + oldValue={ + + } + /> + )} + {reservedRateHasDiff && currentReservedRate && ( + {newReservedRate}% + } + oldValue={{currentReservedRate}%} + /> + )} + {reservedSplitsHasDiff && ( +
+
+ Reserved recipients: +
+ +
+ )} + + } + /> + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/useDetailsSectionValues.ts b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/useDetailsSectionValues.ts new file mode 100644 index 0000000000..e2d7005d31 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/useDetailsSectionValues.ts @@ -0,0 +1,95 @@ +import { getApprovalStrategyByAddress } from 'packages/v4/utils/approvalHooks' +import { otherUnitToSeconds } from 'utils/format/formatTime' +import { useEditCycleFormContext } from '../../EditCycleFormContext' + +export const useDetailsSectionValues = () => { + + const { editCycleForm, initialFormData } = useEditCycleFormContext() + + const currentDuration = otherUnitToSeconds({ + duration: initialFormData?.duration ?? 0, + unit: initialFormData?.durationUnit.value, + }) + + const newDuration = otherUnitToSeconds({ + duration: editCycleForm?.getFieldValue('duration'), + unit: editCycleForm?.getFieldValue('durationUnit')?.value, + }) + + const durationHasDiff = newDuration !== currentDuration + + const newBallot = getApprovalStrategyByAddress( + editCycleForm?.getFieldValue('approvalHook'), + ) + const currentBallot = initialFormData + ? getApprovalStrategyByAddress(initialFormData.approvalHook) + : undefined + const ballotHasDiff = newBallot?.address !== currentBallot?.address + + const newPausePay = Boolean(editCycleForm?.getFieldValue('pausePay')) + const currentPausePay = Boolean(initialFormData?.pausePay) + const pausePayHasDiff = currentPausePay !== newPausePay + + const newSetTerminals = Boolean( + editCycleForm?.getFieldValue('allowSetTerminals'), + ) + const currentSetTerminals = Boolean( + initialFormData?.allowSetTerminals, + ) + const allowSetTerminalsHasDiff = currentSetTerminals !== newSetTerminals + + const newAllowTerminalMigration = Boolean( + editCycleForm?.getFieldValue('allowTerminalMigration'), + ) + const currentAllowTerminalMigration = Boolean( + initialFormData?.allowTerminalMigration, + ) + const allowTerminalMigrationHasDiff = + currentAllowTerminalMigration !== newAllowTerminalMigration + + const newSetController = Boolean( + editCycleForm?.getFieldValue('allowSetController'), + ) + const currentSetController = Boolean( + initialFormData?.allowSetController, + ) + const allowSetControllerHasDiff = currentSetController !== newSetController + + const advancedOptionsHasDiff = + pausePayHasDiff || + allowSetTerminalsHasDiff || + allowSetControllerHasDiff || + allowTerminalMigrationHasDiff + + const sectionHasDiff = + durationHasDiff || ballotHasDiff || advancedOptionsHasDiff + + return { + currentDuration, + newDuration, + durationHasDiff, + + currentBallot, + newBallot, + ballotHasDiff, + + newPausePay, + currentPausePay, + pausePayHasDiff, + + newSetTerminals, + currentSetTerminals, + allowSetTerminalsHasDiff, + + newAllowTerminalMigration, + currentAllowTerminalMigration, + allowTerminalMigrationHasDiff, + + newSetController, + currentSetController, + allowSetControllerHasDiff, + + advancedOptionsHasDiff, + sectionHasDiff, + } +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/usePayoutsSectionValues.ts b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/usePayoutsSectionValues.ts new file mode 100644 index 0000000000..d3179df115 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/usePayoutsSectionValues.ts @@ -0,0 +1,70 @@ +import { WeiPerEther } from '@ethersproject/constants' +import { CurrencyName } from 'constants/currency' +import { JBSplit } from 'juice-sdk-core' +import { distributionLimitsEqual } from 'packages/v4/utils/distributions' +import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math' +import { splitsListsHaveDiff } from 'packages/v4/utils/v4Splits' +import { useEditCycleFormContext } from '../../EditCycleFormContext' + +export const usePayoutsSectionValues = () => { + + const { editCycleForm, initialFormData } = useEditCycleFormContext() + + const newPayoutSplits: JBSplit[] = editCycleForm?.getFieldValue('payoutSplits') ?? [] + const currentPayoutSplits = initialFormData?.payoutSplits ?? [] + const payoutSplitsHasDiff = splitsListsHaveDiff( + currentPayoutSplits, + newPayoutSplits, + ) + + const newCurrency: CurrencyName = editCycleForm?.getFieldValue( + 'distributionLimitCurrency', + ) ?? 'ETH' + const currentCurrency = + initialFormData?.payoutLimitCurrency ?? + 'ETH' + const currencyHasDiff = currentCurrency !== newCurrency + + const newDistributionLimitNum: number = editCycleForm?.getFieldValue('payoutLimit') + const newDistributionLimit = + newDistributionLimitNum ? BigInt(newDistributionLimitNum) * WeiPerEther.toBigInt() : MAX_PAYOUT_LIMIT + + const currentDistributionLimitNum = initialFormData?.payoutLimit + const currentDistributionLimit = currentDistributionLimitNum ? BigInt(currentDistributionLimitNum) * WeiPerEther.toBigInt() : MAX_PAYOUT_LIMIT + + const distributionLimitHasDiff = + !distributionLimitsEqual(currentDistributionLimit, newDistributionLimit) || + currencyHasDiff + // TODO: When no limit is set and doesnt change, distributionLimitHasDiff still true + const distributionLimitIsInfinite = + !newDistributionLimit || newDistributionLimit === MAX_PAYOUT_LIMIT + + const newHoldFees = Boolean(editCycleForm?.getFieldValue('holdFees')) + const currentHoldFees = Boolean(initialFormData?.holdFees) + const holdFeesHasDiff = newHoldFees !== currentHoldFees + + const advancedOptionsHasDiff = holdFeesHasDiff + const sectionHasDiff = + distributionLimitHasDiff || payoutSplitsHasDiff || advancedOptionsHasDiff + + return { + newCurrency, + currentCurrency, + + currentDistributionLimit, + newDistributionLimit, + distributionLimitHasDiff, + distributionLimitIsInfinite, + + currentPayoutSplits, + newPayoutSplits, + payoutSplitsHasDiff, + + newHoldFees, + currentHoldFees, + holdFeesHasDiff, + + advancedOptionsHasDiff, + sectionHasDiff, + } +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/useTokensSectionValues.ts b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/useTokensSectionValues.ts new file mode 100644 index 0000000000..4f3161da98 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/hooks/useTokensSectionValues.ts @@ -0,0 +1,122 @@ +import { useJBTokenContext } from 'juice-sdk-react' +import round from 'lodash/round' +import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' +import { splitsListsHaveDiff } from 'packages/v4/utils/v4Splits' +import { tokenSymbolText } from 'utils/tokenSymbolText' +import { useEditCycleFormContext } from '../../EditCycleFormContext' +import { EditCycleFormFields } from '../../EditCycleFormFields' + +export const useTokensSectionValues = () => { + const { editCycleForm, initialFormData } = useEditCycleFormContext() + + const formValues: EditCycleFormFields = editCycleForm?.getFieldsValue(true) + + const { + ruleset: upcomingRuleset, + } = useJBUpcomingRuleset() + + const { token } = useJBTokenContext() + const tokenSymbol = token?.data?.symbol + + const tokenSymbolPlural = tokenSymbolText({ + tokenSymbol, + capitalize: false, + plural: true, + }) + const newMintRate = formValues.issuanceRate + + const newReservedRate = formValues.reservedPercent + const newReservedSplits = formValues.reservedTokensSplits + const newDiscountRate = formValues.decayPercent + const newRedemptionRate = formValues.redemptionRate + const newAllowMinting = formValues.allowOwnerMinting + const newTokenTransfers = formValues.tokenTransfers + + const currentMintRate = initialFormData?.issuanceRate + const currentReservedRate = initialFormData?.reservedPercent + const currentReservedSplits = initialFormData?.reservedTokensSplits + const currentDiscountRate = initialFormData?.decayPercent + const currentRedemptionRate = initialFormData?.redemptionRate + const currentAllowMinting = Boolean(initialFormData?.allowOwnerMinting) + const currentTokenTransfers = Boolean( + initialFormData?.tokenTransfers, + ) + + const onlyDiscountRateApplied = + upcomingRuleset && + newMintRate && + round(upcomingRuleset?.weight.toFloat(), 4) === round(newMintRate, 4) + + const mintRateHasDiff = !onlyDiscountRateApplied + + const reservedRateHasDiff = Boolean( + currentReservedRate && + newReservedRate !== currentReservedRate, + ) + + const reservedSplitsHasDiff = splitsListsHaveDiff( + currentReservedSplits, + newReservedSplits, + ) + + const discountRateHasDiff = Boolean( + currentDiscountRate && + newDiscountRate !== currentDiscountRate, + ) + + const redemptionHasDiff = + currentRedemptionRate && + newRedemptionRate !== currentRedemptionRate + + const allowMintingHasDiff = Boolean(newAllowMinting !== currentAllowMinting) + + const tokenTransfersHasDiff = Boolean( + newTokenTransfers !== currentTokenTransfers, + ) + + const advancedOptionsHasDiff = + reservedRateHasDiff || + discountRateHasDiff || + redemptionHasDiff || + allowMintingHasDiff || + tokenTransfersHasDiff + + const sectionHasDiff = + mintRateHasDiff || reservedSplitsHasDiff || advancedOptionsHasDiff + + return { + newMintRate, + currentMintRate, + mintRateHasDiff, + + newReservedRate, + currentReservedRate, + reservedRateHasDiff, + + newReservedSplits, + currentReservedSplits, + reservedSplitsHasDiff, + + newDiscountRate, + currentDiscountRate, + discountRateHasDiff, + + newRedemptionRate, + currentRedemptionRate, + redemptionHasDiff, + + newAllowMinting, + currentAllowMinting, + allowMintingHasDiff, + + newTokenTransfers, + currentTokenTransfers, + tokenTransfersHasDiff, + + tokenSymbolPlural, + // unsafeFundingCycleProperties, + + advancedOptionsHasDiff, + sectionHasDiff, + } +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/index.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/index.tsx new file mode 100644 index 0000000000..17f5cfc87e --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/ReviewConfirmModal/index.tsx @@ -0,0 +1 @@ +export * from './ReviewConfirmModal' diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/IssuanceRateReductionField.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/IssuanceRateReductionField.tsx new file mode 100644 index 0000000000..5f472d64b2 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/IssuanceRateReductionField.tsx @@ -0,0 +1,53 @@ +import { Trans } from '@lingui/macro' +import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' +import { JuiceSwitch } from 'components/inputs/JuiceSwitch' +import NumberSlider from 'components/inputs/NumberSlider' +import { useState } from 'react' +import { helpPagePath } from 'utils/helpPagePath' +import { useEditCycleFormContext } from '../EditCycleFormContext' +import { zeroPercentDisabledNoticed } from './RedemptionRateField' + +// Note: "IssuanceRate" = "DecayRate" +export function IssuanceRateReductionField() { + const { editCycleForm, setFormHasUpdated } = useEditCycleFormContext() + + // Issurance reduction rate % + const issuanceReductionRate = + editCycleForm?.getFieldValue('decayPercent') ?? (0 as number) + + const [ + issuanceReductionRateSwitchEnabled, + setIssuanceReductionRateSwitchEnabled, + ] = useState(issuanceReductionRate > 0) + return ( +
+ Enable decay rate} + description={ + + Reduce the total token issuance each cycle.{' '} + + Learn more + + + } + value={issuanceReductionRateSwitchEnabled} + extra={ + issuanceReductionRateSwitchEnabled ? null : zeroPercentDisabledNoticed + } + onChange={val => { + setIssuanceReductionRateSwitchEnabled(val) + setFormHasUpdated(true) + if (!val) { + editCycleForm?.setFieldsValue({ decayPercent: 0 }) + } + }} + /> + {issuanceReductionRateSwitchEnabled ? ( + + ) : null} +
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/MintRateField.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/MintRateField.tsx new file mode 100644 index 0000000000..9b085f13bc --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/MintRateField.tsx @@ -0,0 +1,27 @@ +import { Trans } from '@lingui/macro' +import { Form } from 'antd' +import FormattedNumberInput from 'components/inputs/FormattedNumberInput' +import { MaxUint88 } from 'constants/numbers' + +// @v4todo: add to SDK +const MAX_ISSUANCE_RATE = Math.floor(MaxUint88 / 10 ** 18) + +// Note: "MintRate" = "IssuanceRate" +export function MintRateField() { + return ( + + + tokens per ETH paid + + } + isInteger + /> + + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/RedemptionRateField.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/RedemptionRateField.tsx new file mode 100644 index 0000000000..d0fbb6f64c --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/RedemptionRateField.tsx @@ -0,0 +1,70 @@ +import { Trans } from '@lingui/macro' +import { useWatch } from 'antd/lib/form/Form' +import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' +import { TokenRedemptionRateGraph } from 'components/TokenRedemptionRateGraph/TokenRedemptionRateGraph' +import { JuiceSwitch } from 'components/inputs/JuiceSwitch' +import NumberSlider from 'components/inputs/NumberSlider' +import { useState } from 'react' +import { helpPagePath } from 'utils/helpPagePath' +import { useEditCycleFormContext } from '../EditCycleFormContext' + +export const zeroPercentDisabledNoticed = ( + + (0%) + +) + +export function RedemptionRateField() { + const { editCycleForm, setFormHasUpdated } = useEditCycleFormContext() + + // Redemption rate % + const redemptionReductionRate = useWatch('redemptionRate', editCycleForm) + + const [redemptionRateSwitchEnabled, setRedemptionRateSwitchEnabled] = + useState( + (editCycleForm?.getFieldValue('redemptionRate') ?? 100) > 0, + ) + return ( +
+ Enable redemption rate} + description={ + + Incentivise long-term token holding with a redemption rate.{' '} + + Learn more + + + } + value={redemptionRateSwitchEnabled} + extra={redemptionRateSwitchEnabled ? null : zeroPercentDisabledNoticed} + onChange={val => { + setRedemptionRateSwitchEnabled(val) + setFormHasUpdated(true) + if (!val) { + editCycleForm?.setFieldsValue({ + redemptionRate: 0, + }) + } + }} + /> + {redemptionRateSwitchEnabled ? ( +
+ + +
+ ) : null} +
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/ReservedTokensField.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/ReservedTokensField.tsx new file mode 100644 index 0000000000..63499af2e0 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/ReservedTokensField.tsx @@ -0,0 +1,90 @@ +import { Trans } from '@lingui/macro' +import { useWatch } from 'antd/lib/form/Form' +import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' +import { FormItems } from 'components/formItems' +import { ItemNoInput } from 'components/formItems/ItemNoInput' +import { JuiceSwitch } from 'components/inputs/JuiceSwitch' +import { AdvancedDropdown } from 'components/Project/ProjectSettings/AdvancedDropdown' +import { JBSplit, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' +import { V4EditReservedTokens } from 'packages/v4/components/V4EditReservedTokens' +import { totalSplitsPercent } from 'packages/v4/utils/v4Splits' +import { useState } from 'react' +import { helpPagePath } from 'utils/helpPagePath' +import { useEditCycleFormContext } from '../EditCycleFormContext' +import { zeroPercentDisabledNoticed } from './RedemptionRateField' + +export function ReservedTokensField() { + const { editCycleForm, setFormHasUpdated } = useEditCycleFormContext() + + const reservedTokens = useWatch('reservedPercent', editCycleForm) ?? 0 + const reservedSplits = useWatch('reservedTokensSplits', editCycleForm) ?? [] + const totalIssuanceRate = useWatch('issuanceRate', editCycleForm) ?? 0 + + const reservedSplitsPercentExceedsMax = + totalSplitsPercent(reservedSplits) > SPLITS_TOTAL_PERCENT + + const [reservedTokensSwitchEnabled, setReservedTokensSwitchEnabled] = + useState(editCycleForm?.getFieldValue('reservedPercent') > 0) + return ( +
+ Enable reserved tokens} + description={ + + Reserve a percentage of freshly minted tokens for specified + recipients.{' '} + + Learn more + + + } + value={reservedTokensSwitchEnabled} + onChange={val => { + setReservedTokensSwitchEnabled(val) + setFormHasUpdated(true) + if (!val) { + editCycleForm?.setFieldsValue({ reservedPercent: 0 }) + } + }} + extra={reservedTokensSwitchEnabled ? null : zeroPercentDisabledNoticed} + /> + {reservedTokensSwitchEnabled ? ( +
+ {/* Slider */} + + {/* Reserved token recipient listing */} + Reserved token recipients} + hideDivider + > + { + editCycleForm?.setFieldsValue({ + reservedTokensSplits: splits, + }) + setFormHasUpdated(true) + }} + hideTitle + /> + +
+ ) : null} + {reservedSplitsPercentExceedsMax ? ( + + Reserved recipients cannot exceed 100% + + ) : null} + {/* Empty inputs to keep AntD useWatch happy */} + +
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/TokensSection.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/TokensSection.tsx new file mode 100644 index 0000000000..4e961c6e62 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/TokensSection.tsx @@ -0,0 +1,26 @@ +import { Trans } from '@lingui/macro' +import { EditCycleHeader } from 'components/Project/ProjectSettings/EditCycleHeader' +import { MintRateField } from './MintRateField' +import { TokensSectionAdvanced } from './TokensSectionAdvanced' + +export function TokensSection() { + return ( + <> +
+ Total issuance} + description={ + + Enter a number of tokens that your project contributors will + receive when they pay your project. You can use these tokens for + governance rights, community access, or other perks such as ETH + redemption. + + } + /> + +
+ + + ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/TokensSectionAdvanced.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/TokensSectionAdvanced.tsx new file mode 100644 index 0000000000..593f4f7927 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/TokensSectionAdvanced.tsx @@ -0,0 +1,36 @@ +import { Trans } from '@lingui/macro' +import { Form } from 'antd' +import { JuiceSwitch } from 'components/inputs/JuiceSwitch' +import { AdvancedDropdown } from 'components/Project/ProjectSettings/AdvancedDropdown' +import { IssuanceRateReductionField } from './IssuanceRateReductionField' +import { RedemptionRateField } from './RedemptionRateField' +import { ReservedTokensField } from './ReservedTokensField' + +export function TokensSectionAdvanced() { + const advancedChildrenClasses = + '[&>*]:border-b [&>*]:border-grey-300 [&>*]:dark:border-grey-600 [&>*]:pb-7' + + return ( + +
+ + + + + Enable project owner token minting} + description={ + + Project owner can mint tokens for themselves and others at any + time. + + } + /> + + + Enable project token transfers} /> + +
+
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/index.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/index.tsx new file mode 100644 index 0000000000..2692d95442 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TokensSection/index.tsx @@ -0,0 +1 @@ +export * from './TokensSection' diff --git a/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TransactionSuccessModal.tsx b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TransactionSuccessModal.tsx new file mode 100644 index 0000000000..e7e76302e8 --- /dev/null +++ b/src/packages/v4/views/V4ProjectSettings/EditCyclePage/TransactionSuccessModal.tsx @@ -0,0 +1,57 @@ +import { CheckCircleIcon } from '@heroicons/react/24/outline' +import { Trans } from '@lingui/macro' +import { Button, Modal } from 'antd' +import { useJBContractContext } from 'juice-sdk-react' +import Link from 'next/link' +import { settingsPagePath, v4ProjectRoute } from 'packages/v4/utils/routes' +import { ReactNode } from 'react' +import { useChainId } from 'wagmi' + +export function TransactionSuccessModal({ + open, + onClose, + content, +}: { + open: boolean + onClose: VoidFunction + content: ReactNode +}) { + const { projectId: projectIdBigInt } = useJBContractContext() + const projectId = Number(projectIdBigInt) + const chainId = useChainId() + + const checkIconWithBackground = ( +
+
+ +
+
+ ) + + const buttonClasses = 'w-[185px] h-12' + return ( + +
+ {checkIconWithBackground} + {content} +
+ + + + + + +
+
+
+ ) +} diff --git a/src/packages/v4/views/V4ProjectSettings/ProjectSettingsContent.tsx b/src/packages/v4/views/V4ProjectSettings/ProjectSettingsContent.tsx index 71dc282669..b15fb65127 100644 --- a/src/packages/v4/views/V4ProjectSettings/ProjectSettingsContent.tsx +++ b/src/packages/v4/views/V4ProjectSettings/ProjectSettingsContent.tsx @@ -3,10 +3,8 @@ import { ArrowLeftIcon } from '@heroicons/react/24/outline' import { Trans, t } from '@lingui/macro' import { Button, Layout } from 'antd' import Link from 'next/link' -import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' -import { useContext, useMemo } from 'react' +import { useMemo } from 'react' import { twJoin } from 'tailwind-merge' -import { isZeroAddress } from 'utils/address' import { EditCyclePage } from './EditCyclePage/EditCyclePage' import { useSettingsPagePath } from './hooks/useSettingsPagePath' import { ProjectDetailsSettingsPage } from './ProjectDetailsSettingsPage/ProjectDetailsSettingsPage' @@ -84,16 +82,13 @@ export function ProjectSettingsContent({ }: { settingsPageKey: SettingsPageKey }) { - const { fundingCycleMetadata } = useContext(V2V3ProjectContext) - const hasExistingNfts = !isZeroAddress(fundingCycleMetadata?.dataSource) - const ActiveSettingsPage = useMemo( () => SettingsPageComponents[settingsPageKey], [settingsPageKey], ) - const pageTitle = - V4SettingsPageKeyTitleMap(hasExistingNfts)[settingsPageKey] + // const hasExistingNfts = !isZeroAddress(fundingCycleMetadata?.dataSource) + const pageTitle = V4SettingsPageKeyTitleMap(false)[settingsPageKey] return ( diff --git a/src/pages/api/projects/trending.ts b/src/pages/api/projects/trending.ts index a34eaefb7d..321fde416b 100644 --- a/src/pages/api/projects/trending.ts +++ b/src/pages/api/projects/trending.ts @@ -1,4 +1,6 @@ import { PV_V1, PV_V2 } from 'constants/pv' +import { RomanStormVariables } from 'constants/romanStorm' +import { BigNumber } from 'ethers' import { OrderDirection, Project_OrderBy, @@ -47,11 +49,51 @@ const handler: NextApiHandler = async (req, res) => { }, }) + const projects = [...projectsRes.data.projects] + + try { + const romanProjectIndex = projects.findIndex( + el => el.projectId === RomanStormVariables.PROJECT_ID && el.pv === '2', + ) + + if (romanProjectIndex >= 0) { + const filteredProjects = await serverClient.query< + TrendingProjectsQuery, + QueryProjectsArgs + >({ + fetchPolicy: 'no-cache', + query: TrendingProjectsDocument, + variables: { + where: { + pv: '2', + projectId: RomanStormVariables.PROJECT_ID, + }, + block: { + number: RomanStormVariables.SNAPSHOT_BLOCK, + }, + }, + }) + + const [romanProjectSnapshot] = filteredProjects.data.projects + const romanProject = projects[romanProjectIndex] + + projects[romanProjectIndex] = { + ...projects[romanProjectIndex], + volume: BigNumber.from(romanProject.volume ?? 0).sub( + BigNumber.from(romanProjectSnapshot.volume ?? 0), + ), // Incorrect types are declared + paymentsCount: + romanProject.paymentsCount - romanProjectSnapshot.paymentsCount, + } + } + } catch {} + res.setHeader( 'Cache-Control', `s-maxage=${CACHE_MAXAGE}, stale-while-revalidate`, ) - return res.status(200).json(projectsRes.data.projects) + + return res.status(200).json(projects) } catch (e) { console.error(e) return res.status(500).json({ error: 'Something went wrong' }) diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 074a0c3dd1..7a29a18eef 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -1,9 +1,9 @@ import { AppWrapper } from 'components/common/CoreAppWrapper/CoreAppWrapper' import { Head } from 'components/common/Head/Head' -import { Create } from 'components/Create/Create' import { CV_V3 } from 'constants/cv' import { SiteBaseUrl } from 'constants/url' import { TransactionProvider } from 'contexts/Transaction/TransactionProvider' +import { Create } from 'packages/v2v3/components/Create/Create' import { V2V3ContractsProvider } from 'packages/v2v3/contexts/Contracts/V2V3ContractsProvider' import { V2V3CurrencyProvider } from 'packages/v2v3/contexts/V2V3CurrencyProvider' import { Provider } from 'react-redux' diff --git a/src/pages/v4/[chainName]/p/[projectId]/index.tsx b/src/pages/v4/[chainName]/p/[projectId]/index.tsx index 22729b648e..0bf163c12e 100644 --- a/src/pages/v4/[chainName]/p/[projectId]/index.tsx +++ b/src/pages/v4/[chainName]/p/[projectId]/index.tsx @@ -6,7 +6,7 @@ import { useRouter } from 'next/router' import { ReduxProjectCartProvider } from 'packages/v4/components/ProjectDashboard/ReduxProjectCartProvider' import store from 'packages/v4/components/ProjectDashboard/redux/store' import V4ProjectMetadataProvider from 'packages/v4/contexts/V4ProjectMetadataProvider' -import { chainNameMap } from 'packages/v4/utils/networks' +import { useCurrentRouteChainId } from 'packages/v4/hooks/useCurrentRouteChainId' import { V4ProjectDashboard } from 'packages/v4/views/V4ProjectDashboard/V4ProjectDashboard' import { wagmiConfig } from 'packages/v4/wagmiConfig' import React, { PropsWithChildren } from 'react' @@ -41,11 +41,11 @@ const _Wrapper: React.FC = ({ children }) => { export default function V4ProjectPage() { const router = useRouter() - const { chainName, projectId } = router.query - if (!chainName || !projectId) { + const { projectId } = router.query + const chainId = useCurrentRouteChainId() + if (!chainId || !projectId) { return
Invalid URL
} - const chainId = chainNameMap[chainName as string] return ( <_Wrapper> diff --git a/src/redux/slices/editingV2Project/editingV2Project.ts b/src/redux/slices/editingV2Project/editingV2Project.ts index 4cd4d38807..cc0279d0ae 100644 --- a/src/redux/slices/editingV2Project/editingV2Project.ts +++ b/src/redux/slices/editingV2Project/editingV2Project.ts @@ -1,6 +1,4 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { AmountInputValue } from 'components/Create/components/pages/ProjectDetails/ProjectDetailsPage' -import { projectTokenSettingsToReduxFormat } from 'components/Create/utils/projectTokenSettingsToReduxFormat' import { CreatePage } from 'models/createPage' import { JB721GovernanceType, @@ -13,6 +11,8 @@ import { ProjectTagName } from 'models/project-tags' import { ProjectTokensSelection } from 'models/projectTokenSelection' import { ReconfigurationStrategy } from 'models/reconfigurationStrategy' import { TreasurySelection } from 'models/treasurySelection' +import { AmountInputValue } from 'packages/v2v3/components/Create/components/pages/ProjectDetails/ProjectDetailsPage' +import { projectTokenSettingsToReduxFormat } from 'packages/v2v3/components/Create/utils/projectTokenSettingsToReduxFormat' import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' import { Split } from 'packages/v2v3/models/splits' import { diff --git a/src/utils/format/formatTime.ts b/src/utils/format/formatTime.ts index 5c76fa4ce6..95a75032f1 100644 --- a/src/utils/format/formatTime.ts +++ b/src/utils/format/formatTime.ts @@ -63,7 +63,7 @@ export const otherUnitToSeconds = ({ unit, }: { duration: number - unit: DurationUnitsOption + unit: DurationUnitsOption | undefined }) => { if (!duration) return 0 switch (unit) {