diff --git a/.github/workflows/ci-run.yml b/.github/workflows/ci-run.yml index 861018613c..ae27e6db48 100644 --- a/.github/workflows/ci-run.yml +++ b/.github/workflows/ci-run.yml @@ -9,11 +9,9 @@ on: - synchronize branches: - main - - dev push: branches: - main - - dev # manual trigger from Github UI - Action tab workflow_dispatch: diff --git a/.github/workflows/sepolia.yml b/.github/workflows/sepolia.yml index d71b89ff0b..74d1fa86b2 100644 --- a/.github/workflows/sepolia.yml +++ b/.github/workflows/sepolia.yml @@ -3,7 +3,7 @@ name: Deploy Migrations to Sepolia on: push: branches: - - dev + - main workflow_dispatch: jobs: diff --git a/package.json b/package.json index 7cc5c11c61..7cc642904d 100644 --- a/package.json +++ b/package.json @@ -109,8 +109,8 @@ "graphql": "^16.8.1", "he": "^1.2.0", "jsonwebtoken": "^9.0.0", - "juice-sdk-core": "^10.0.3-alpha", - "juice-sdk-react": "^10.0.1-alpha", + "juice-sdk-core": "^9.1.3-alpha", + "juice-sdk-react": "^9.2.3-alpha", "juicebox-metadata-helper": "0.1.7", "less": "4.1.2", "lodash": "^4.17.21", diff --git a/src/components/ActivityList.tsx b/src/components/ActivityList.tsx index 3539a197e9..422ea0fe94 100644 --- a/src/components/ActivityList.tsx +++ b/src/components/ActivityList.tsx @@ -12,12 +12,12 @@ import { import { useMemo, useState } from 'react' import { AnyProjectEvent } from './activityEventElems/AnyProjectEvent' -export interface ActivityOption { +interface ActivityOption { label: string value: ProjectEventFilter } -export const ALL_OPT = (): ActivityOption => ({ label: t`All activity`, value: 'all' }) +const ALL_OPT = (): ActivityOption => ({ label: t`All activity`, value: 'all' }) const PV1_OPTS = (): ActivityOption[] => [ ALL_OPT(), diff --git a/src/packages/v2v3/components/shared/PayoutsTable/ConvertAmountsModal.tsx b/src/components/Create/components/pages/PayoutsPage/components/ConvertAmountsModal.tsx similarity index 95% rename from src/packages/v2v3/components/shared/PayoutsTable/ConvertAmountsModal.tsx rename to src/components/Create/components/pages/PayoutsPage/components/ConvertAmountsModal.tsx index 6f1eb9f189..37b82e035c 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/ConvertAmountsModal.tsx +++ b/src/components/Create/components/pages/PayoutsPage/components/ConvertAmountsModal.tsx @@ -2,12 +2,12 @@ import { t, Trans } from '@lingui/macro' import { Divider, Modal } from 'antd' import CurrencySwitch from 'components/currency/CurrencySwitch' import EthereumAddress from 'components/EthereumAddress' -import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' import { Parenthesis } from 'components/Parenthesis' +import { Split } from 'models/splits' import V2V3ProjectHandleLink from 'packages/v2v3/components/shared/V2V3ProjectHandleLink' +import { ExternalLinkWithIcon } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' -import { Split } from 'packages/v2v3/models/splits' import { V2V3_CURRENCY_ETH, V2V3_CURRENCY_USD, @@ -18,8 +18,6 @@ import { } from 'packages/v2v3/utils/distributions' import { formatCurrencyAmount } from 'packages/v2v3/utils/formatCurrencyAmount' import { SPLITS_TOTAL_PERCENT } from 'packages/v2v3/utils/math' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' -import { isProjectSplit } from 'packages/v2v3/utils/v2v3Splits' import { ReactNode, useCallback, useMemo, useState } from 'react' import { ReduxDistributionLimit, @@ -28,6 +26,8 @@ import { import { parseWad } from 'utils/format/formatNumber' import { formatPercent } from 'utils/format/formatPercent' import { helpPagePath } from 'utils/routes' +import { isProjectSplit } from 'utils/splits' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' export const ConvertAmountsModal = ({ open, diff --git a/src/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx b/src/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx index 1cafe219a4..0e87fffee1 100644 --- a/src/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx +++ b/src/components/Create/components/pages/PayoutsPage/components/CreateFlowPayoutsTable.tsx @@ -1,16 +1,16 @@ import { Form } from 'antd' import { CURRENCY_METADATA, CurrencyName } from 'constants/currency' +import { Split } from 'models/splits' import { PayoutsTable } from 'packages/v2v3/components/shared/PayoutsTable/PayoutsTable' -import { Split } from 'packages/v2v3/models/splits' import { V2V3CurrencyName, getV2V3CurrencyOption, } from 'packages/v2v3/utils/currency' import { MAX_DISTRIBUTION_LIMIT } from 'packages/v2v3/utils/math' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { ReactNode } from 'react' import { useEditingDistributionLimit } from 'redux/hooks/useEditingDistributionLimit' import { fromWad, parseWad } from 'utils/format/formatNumber' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' import { usePayoutsForm } from '../hooks/usePayoutsForm' const DEFAULT_CURRENCY_NAME = CURRENCY_METADATA.ETH.name diff --git a/src/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx b/src/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx index 0389eae6df..02d4c3b212 100644 --- a/src/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx +++ b/src/components/Create/components/pages/PayoutsPage/components/TreasuryOptionsRadio.tsx @@ -3,16 +3,16 @@ import { RadioGroup } from '@headlessui/react' import { t } from '@lingui/macro' import { Callout } from 'components/Callout/Callout' import { DeleteConfirmationModal } from 'components/modals/DeleteConfirmationModal' -import { SwitchToUnlimitedModal } from 'components/PayoutsTable/SwitchToUnlimitedModal' import { useModal } from 'hooks/useModal' import { TreasurySelection } from 'models/treasurySelection' -import { ConvertAmountsModal } from 'packages/v2v3/components/shared/PayoutsTable/ConvertAmountsModal' import { usePayoutsTable } from 'packages/v2v3/components/shared/PayoutsTable/hooks/usePayoutsTable' +import { SwitchToUnlimitedModal } from 'packages/v2v3/components/shared/PayoutsTable/modals/SwitchToUnlimitedModal' import { useCallback, useEffect, useMemo, useState } from 'react' import { useAppSelector } from 'redux/hooks/useAppSelector' import { ReduxDistributionLimit } from 'redux/hooks/useEditingDistributionLimit' import { fromWad } from 'utils/format/formatNumber' import { Icons } from '../../../Icons' +import { ConvertAmountsModal } from './ConvertAmountsModal' import { RadioCard } from './RadioCard' const treasuryOptions = () => [ diff --git a/src/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts b/src/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts index 8a7b6c80fe..4b7554d0d5 100644 --- a/src/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts +++ b/src/components/Create/components/pages/PayoutsPage/hooks/usePayoutsForm.ts @@ -1,11 +1,11 @@ import { Form } from 'antd' import { TreasurySelection } from 'models/treasurySelection' import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { useDebugValue, useEffect, useMemo } from 'react' import { useAppDispatch } from 'redux/hooks/useAppDispatch' import { useAppSelector } from 'redux/hooks/useAppSelector' import { useEditingPayoutSplits } from 'redux/hooks/useEditingPayoutSplits' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' type PayoutsFormProps = Partial<{ selection: TreasurySelection diff --git a/src/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts b/src/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts index 9e743f07aa..ee1022dd92 100644 --- a/src/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts +++ b/src/components/Create/components/pages/ProjectToken/hooks/useProjectTokenForm.ts @@ -14,13 +14,13 @@ import { redemptionRateFrom, reservedRateFrom, } from 'packages/v2v3/utils/math' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { useDebugValue, useEffect, useMemo } from 'react' import { useAppDispatch } from 'redux/hooks/useAppDispatch' import { useAppSelector } from 'redux/hooks/useAppSelector' import { useEditingDistributionLimit } from 'redux/hooks/useEditingDistributionLimit' import { useEditingReservedTokensSplits } from 'redux/hooks/useEditingReservedTokensSplits' import { editingV2ProjectActions } from 'redux/slices/editingV2Project' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' import { useFormDispatchWatch } from '../../hooks/useFormDispatchWatch' export type ProjectTokensFormProps = Partial<{ diff --git a/src/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx b/src/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx index c7132a21d9..512a7cb798 100644 --- a/src/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx +++ b/src/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx @@ -7,10 +7,10 @@ import ExternalLink from 'components/ExternalLink' import TransactionModal from 'components/modals/TransactionModal' import { TERMS_OF_SERVICE_URL } from 'constants/links' import { useWallet } from 'hooks/Wallet' -import { emitConfirmationDeletionModal } from 'hooks/emitConfirmationDeletionModal' import useMobile from 'hooks/useMobile' import { useModal } from 'hooks/useModal' import { useRouter } from 'next/router' +import { emitConfirmationDeletionModal } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/utils/modals' 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/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts b/src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts index 99ccac1c6d..b6d77e1196 100644 --- a/src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts +++ b/src/components/Create/components/pages/ReviewDeploy/components/FundingConfigurationReview/hooks/useFundingConfigurationReview.ts @@ -3,13 +3,13 @@ import { useAvailablePayoutsSelections } from 'components/Create/components/page import { formatFundingCycleDuration } from 'components/Create/utils/formatFundingCycleDuration' import moment from 'moment' import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { useCallback, useMemo } from 'react' import { useAppSelector } from 'redux/hooks/useAppSelector' import { useEditingDistributionLimit } from 'redux/hooks/useEditingDistributionLimit' import { useEditingPayoutSplits } from 'redux/hooks/useEditingPayoutSplits' import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project' import { formatFundingTarget } from 'utils/format/formatFundingTarget' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' export const useFundingConfigurationReview = () => { const { fundingCycleData, payoutsSelection, mustStartAtOrAfter } = diff --git a/src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts b/src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts index c5f4b5da21..4e1e8058b1 100644 --- a/src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts +++ b/src/components/Create/components/pages/ReviewDeploy/components/ProjectTokenReview/hooks/useProjectTokenReview.ts @@ -1,9 +1,9 @@ import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { useCallback, useMemo } from 'react' import { useAppSelector } from 'redux/hooks/useAppSelector' import { useEditingReservedTokensSplits } from 'redux/hooks/useEditingReservedTokensSplits' import { formatEnabled, formatPaused } from 'utils/format/formatBoolean' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' export const useProjectTokenReview = () => { const { diff --git a/src/components/Create/utils/projectTokenSettingsToReduxFormat.ts b/src/components/Create/utils/projectTokenSettingsToReduxFormat.ts index de6f55ff2d..5f9255582f 100644 --- a/src/components/Create/utils/projectTokenSettingsToReduxFormat.ts +++ b/src/components/Create/utils/projectTokenSettingsToReduxFormat.ts @@ -4,8 +4,8 @@ import { redemptionRateFrom, reservedRateFrom, } from 'packages/v2v3/utils/math' -import { allocationToSplit } from 'packages/v2v3/utils/splitToAllocation' import { EMPTY_RESERVED_TOKENS_GROUPED_SPLITS } from 'redux/slices/editingV2Project' +import { allocationToSplit } from 'utils/splitToAllocation' import { ProjectTokensFormProps } from '../components/pages/ProjectToken/hooks/useProjectTokenForm' export const projectTokenSettingsToReduxFormat = ( diff --git a/src/packages/v2v3/components/shared/FeeTooltipLabel.tsx b/src/components/FeeTooltipLabel.tsx similarity index 87% rename from src/packages/v2v3/components/shared/FeeTooltipLabel.tsx rename to src/components/FeeTooltipLabel.tsx index 56134f3b71..ce47f92e4f 100644 --- a/src/packages/v2v3/components/shared/FeeTooltipLabel.tsx +++ b/src/components/FeeTooltipLabel.tsx @@ -1,14 +1,14 @@ import { Trans } from '@lingui/macro' -import ExternalLink from 'components/ExternalLink' -import TooltipLabel from 'components/TooltipLabel' -import CurrencySymbol from 'components/currency/CurrencySymbol' -import ETHAmount from 'components/currency/ETHAmount' import { BigNumber } from 'ethers' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' import { V2V3_CURRENCY_ETH } from 'packages/v2v3/utils/currency' import { amountSubFee, formatFee } from 'packages/v2v3/utils/math' import { formatWad } from 'utils/format/formatNumber' import { helpPagePath } from 'utils/routes' +import ExternalLink from './ExternalLink' +import TooltipLabel from './TooltipLabel' +import CurrencySymbol from './currency/CurrencySymbol' +import ETHAmount from './currency/ETHAmount' export const FeeTooltipLabel = ({ currency, diff --git a/src/components/Navbar/components/TransactionList/TransactionsList.tsx b/src/components/Navbar/components/TransactionList/TransactionsList.tsx index 1234d12ca9..f1377ec1fc 100644 --- a/src/components/Navbar/components/TransactionList/TransactionsList.tsx +++ b/src/components/Navbar/components/TransactionList/TransactionsList.tsx @@ -5,8 +5,8 @@ import BadgeIcon from 'components/BadgeIcon' import ExternalLink from 'components/ExternalLink' import Loading from 'components/Loading' import { - TransactionLog, TxHistoryContext, + timestampForTxLog, } from 'contexts/Transaction/TxHistoryContext' import { TxStatus } from 'models/transaction' import { useContext, useEffect, useMemo, useState } from 'react' @@ -15,11 +15,6 @@ import { etherscanLink } from 'utils/etherscan' import { formatHistoricalDate } from 'utils/format/formatDate' import TxStatusIcon from './TxStatusIcon' -// Prefer using tx.timestamp if tx has been mined. Otherwise use createdAt timestamp -export const timestampForTxLog = (txLog: TransactionLog) => { - return txLog.tx?.timestamp ?? txLog.createdAt -} - export function TransactionsList({ listClassName, }: { diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderStats.test.tsx b/src/components/Project/ProjectHeader/ProjectHeaderStats.test.tsx similarity index 100% rename from src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderStats.test.tsx rename to src/components/Project/ProjectHeader/ProjectHeaderStats.test.tsx diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderStats.tsx b/src/components/Project/ProjectHeader/ProjectHeaderStats.tsx similarity index 96% rename from src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderStats.tsx rename to src/components/Project/ProjectHeader/ProjectHeaderStats.tsx index fdd529b0d7..22e78a378a 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderStats.tsx +++ b/src/components/Project/ProjectHeader/ProjectHeaderStats.tsx @@ -2,12 +2,12 @@ import { BigNumber } from '@ethersproject/bignumber' import { ArrowTrendingUpIcon } from '@heroicons/react/24/outline' import { t, Trans } from '@lingui/macro' import ETHAmount from 'components/currency/ETHAmount' -import { ProjectHeaderStat } from 'components/Project/ProjectHeader/ProjectHeaderStat' import { TRENDING_WINDOW_DAYS } from 'components/Projects/RankingExplanation' import { useProjectPageQueries } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectPageQueries' import { useV2V3ProjectHeader } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useV2V3ProjectHeader' import { PropsWithChildren, useCallback } from 'react' import { twMerge } from 'tailwind-merge' +import { ProjectHeaderStat } from './ProjectHeaderStat' export function ProjectHeaderStats() { const { payments, totalVolume, last7DaysPercent } = useV2V3ProjectHeader() diff --git a/src/components/Project/ProjectTabs/utils/pairToDatum.test.ts b/src/components/Project/ProjectHeader/utils/pairToDatum.test.ts similarity index 100% rename from src/components/Project/ProjectTabs/utils/pairToDatum.test.ts rename to src/components/Project/ProjectHeader/utils/pairToDatum.test.ts diff --git a/src/components/Project/ProjectTabs/utils/pairToDatum.ts b/src/components/Project/ProjectHeader/utils/pairToDatum.ts similarity index 100% rename from src/components/Project/ProjectTabs/utils/pairToDatum.ts rename to src/components/Project/ProjectHeader/utils/pairToDatum.ts diff --git a/src/components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel.tsx b/src/components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel.tsx index b23135cc07..7c22abd2eb 100644 --- a/src/components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel.tsx +++ b/src/components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel.tsx @@ -16,9 +16,9 @@ export type ConfigurationPanelTableData = { type ConfigurationPanelProps = { cycle: ConfigurationPanelTableData - token: ConfigurationPanelTableData - otherRules: ConfigurationPanelTableData - extension: ConfigurationPanelTableData | null + token?: ConfigurationPanelTableData // V4TODO: don't make token optional + otherRules?: ConfigurationPanelTableData // V4TODO: don't make otherRules optional + extension?: ConfigurationPanelTableData | null // V4TODO: don't make extension optional } export const ConfigurationPanel: React.FC = ({ @@ -30,8 +30,12 @@ export const ConfigurationPanel: React.FC = ({ return (
- - + {token && ( // V4TODO: don't make token optional + + )} + {otherRules && ( // V4TODO: don't make otherRules optional + + )} {extension && ( )} diff --git a/src/components/ProjectSafeDashboard/juiceboxTransactions/reconfigureFundingCyclesOf/ReconfigurationRichPreview.tsx b/src/components/ProjectSafeDashboard/juiceboxTransactions/reconfigureFundingCyclesOf/ReconfigurationRichPreview.tsx index c0ed95e809..ca5b6a10e3 100644 --- a/src/components/ProjectSafeDashboard/juiceboxTransactions/reconfigureFundingCyclesOf/ReconfigurationRichPreview.tsx +++ b/src/components/ProjectSafeDashboard/juiceboxTransactions/reconfigureFundingCyclesOf/ReconfigurationRichPreview.tsx @@ -7,8 +7,8 @@ import FundingCycleDetails from 'packages/v2v3/components/V2V3Project/V2V3Fundin import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { deriveNextIssuanceRate } from 'packages/v2v3/utils/fundingCycle' import { formatReservedRate } from 'packages/v2v3/utils/math' -import { toSplit } from 'packages/v2v3/utils/v2v3Splits' import { useContext } from 'react' +import { toSplit } from 'utils/splits' import { LinkToSafeButton } from '../../LinkToSafeButton' import { useTransactionJBController } from './hooks/useTransactionJBController' diff --git a/src/components/VolumeChart/hooks/useProjectTimeline.ts b/src/components/VolumeChart/hooks/useProjectTimeline.ts index 835bd277ed..8118da562a 100644 --- a/src/components/VolumeChart/hooks/useProjectTimeline.ts +++ b/src/components/VolumeChart/hooks/useProjectTimeline.ts @@ -67,24 +67,16 @@ export function useProjectTimeline({ return { blocks, timestamps } }, [blockData]) - const { data: v1v2v3QueryResult, loading: isLoadingQuery } = useProjectTlQuery({ + const { data: queryResult, loading: isLoadingQuery } = useProjectTlQuery({ client, variables: { id: blocks ? getSubgraphIdForProject(pv, projectId) : '', ...blocks, }, }) - // TODO: const { data: v4QueryResult } = useSubgraphQuery(ProjectTlDocument, { - // where: { - // projectId, - // }, - // }) const points = useMemo(() => { - // TODO: if (!(v1v2v3QueryResult || v4QueryResult) || !timestamps) return - if (!v1v2v3QueryResult || !timestamps) return - // TODO: const queryResult = pv === PV_V4 ? v4QueryResult : v1v2v3QueryResult - const queryResult = v1v2v3QueryResult + if (!queryResult || !timestamps) return const points: ProjectTimelinePoint[] = [] @@ -102,7 +94,7 @@ export function useProjectTimeline({ } return points - }, [timestamps, v1v2v3QueryResult]) + }, [timestamps, queryResult]) return { points, diff --git a/src/components/formItems/ProjectDiscord.tsx b/src/components/formItems/ProjectDiscord.tsx index e9d234fd94..1068186e50 100644 --- a/src/components/formItems/ProjectDiscord.tsx +++ b/src/components/formItems/ProjectDiscord.tsx @@ -14,7 +14,7 @@ export default function ProjectDiscord({ label={hideLabel ? undefined : t`Discord link`} {...formItemProps} > - + ) } diff --git a/src/components/formItems/ProjectTelegram.tsx b/src/components/formItems/ProjectTelegram.tsx index c2ff1b2993..821c7211de 100644 --- a/src/components/formItems/ProjectTelegram.tsx +++ b/src/components/formItems/ProjectTelegram.tsx @@ -14,7 +14,7 @@ export default function ProjectTelegam({ label={hideLabel ? undefined : t`Telegram link`} {...formItemProps} > - + ) } diff --git a/src/components/formItems/formHelpers.tsx b/src/components/formItems/formHelpers.tsx index 93ec60e734..70864f8a95 100644 --- a/src/components/formItems/formHelpers.tsx +++ b/src/components/formItems/formHelpers.tsx @@ -2,7 +2,7 @@ import { isAddress } from 'ethers/lib/utils' import { PayoutMod } from 'packages/v1/models/mods' import { permyriadToPercent } from 'utils/format/formatNumber' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { isEqualAddress, isZeroAddress } from 'utils/address' import { percentToPermyriad } from 'utils/format/formatNumber' diff --git a/src/constants/splits.ts b/src/constants/splits.ts index edb6bde1bb..c0084a5c0c 100644 --- a/src/constants/splits.ts +++ b/src/constants/splits.ts @@ -1,4 +1,4 @@ -import { ETHPayoutSplitGroup, ReservedTokensSplitGroup } from 'packages/v2v3/models/splits' +import { ETHPayoutSplitGroup, ReservedTokensSplitGroup } from 'models/splits' export const ETH_PAYOUT_SPLIT_GROUP: ETHPayoutSplitGroup = 1 export const RESERVED_TOKEN_SPLIT_GROUP: ReservedTokensSplitGroup = 2 diff --git a/src/contexts/Transaction/EthersTxHistoryProvider.tsx b/src/contexts/Transaction/EthersTxHistoryProvider.tsx index 4c2b9a9656..73b6f16a80 100644 --- a/src/contexts/Transaction/EthersTxHistoryProvider.tsx +++ b/src/contexts/Transaction/EthersTxHistoryProvider.tsx @@ -1,8 +1,7 @@ import { readProvider } from 'constants/readProvider' -import { TxStatus } from 'models/transaction' +import { TransactionLog, TxStatus } from 'models/transaction' import { ReactNode, useEffect } from 'react' -import { Hash } from 'viem' -import { TransactionLog, TxHistoryContext } from './TxHistoryContext' +import { TxHistoryContext } from './TxHistoryContext' import { useTransactions } from './useTransactions' const nowSeconds = () => Math.round(new Date().valueOf() / 1000) @@ -46,10 +45,7 @@ const pollTransaction = async ( txLog.callbacks?.onConfirmed?.(response) return { ...txLog, - tx: { - hash: response.hash as Hash, - timestamp: response.timestamp, - }, + tx: response, status: TxStatus.success, } } diff --git a/src/contexts/Transaction/TxHistoryContext.ts b/src/contexts/Transaction/TxHistoryContext.ts index ac93e5a0ea..21bf495374 100644 --- a/src/contexts/Transaction/TxHistoryContext.ts +++ b/src/contexts/Transaction/TxHistoryContext.ts @@ -1,24 +1,17 @@ -import { TransactionCallbacks, TxStatus } from 'models/transaction' +import { providers } from 'ethers' +import { TransactionCallbacks, TransactionLog } from 'models/transaction' import { createContext } from 'react' -import { Hash } from 'viem' -export type TransactionType = { - hash: Hash - timestamp?: number -} - -export type TransactionLog = { - id: number - title: string - createdAt: number - tx: TransactionType | null - status: TxStatus.pending | TxStatus.success | TxStatus.failed - callbacks?: TransactionCallbacks +// Prefer using tx.timestamp if tx has been mined. Otherwise use createdAt timestamp +export const timestampForTxLog = (txLog: TransactionLog) => { + return ( + (txLog.tx as providers.TransactionResponse)?.timestamp ?? txLog.createdAt + ) } export type AddTransactionFunction = ( title: string, - tx: TransactionType, + tx: providers.TransactionResponse, callbacks?: Omit, ) => void diff --git a/src/contexts/Transaction/WagmiTxHistoryProvider.tsx b/src/contexts/Transaction/WagmiTxHistoryProvider.tsx index 2691c56f78..d707751d01 100644 --- a/src/contexts/Transaction/WagmiTxHistoryProvider.tsx +++ b/src/contexts/Transaction/WagmiTxHistoryProvider.tsx @@ -1,94 +1,14 @@ -import { getTransactionReceipt } from '@wagmi/core' -import { TxStatus } from 'models/transaction' -import { wagmiConfig } from 'packages/v4/wagmiConfig' -import { ReactNode, useEffect } from 'react' -import { TransactionLog, TxHistoryContext } from './TxHistoryContext' +import { ReactNode } from 'react' +import { TxHistoryContext } from './TxHistoryContext' import { useTransactions } from './useTransactions' -const nowSeconds = () => Math.round(new Date().valueOf() / 1000) - -const SHORT_POLL_INTERVAL_MILLISECONDS = 3 * 1000 // 3 sec -const LONG_POLL_INTERVAL_MILLISECONDS = 12 * 1000 // 12 sec - -const pollTransaction = async ( - txLog: TransactionLog, -): Promise => { - // Only do refresh logic for pending txs - // tx.hash shouldn't ever be undefined but it's optional typed :shrug: - if (!txLog.tx?.hash) { - return txLog - } - - try { - // this throws if the tx hasnt been mined - const response = await getTransactionReceipt(wagmiConfig, { - hash: txLog.tx.hash, - }) - - return { - ...txLog, - status: - response.status === 'success' ? TxStatus.success : TxStatus.failed, - } - } catch { - return txLog - } - - return txLog -} - export default function WagmiTxHistoryProvider({ children, }: { children: ReactNode }) { - const { transactions, addTransaction, removeTransaction, setTransactions } = - useTransactions() - - // Setup poller for refreshing transactions - useEffect(() => { - // Only set new poller if there are pending transactions - // Succeeded/failed txs don't need to be refreshed - if (!transactions.some(tx => tx.status === TxStatus.pending)) { - return - } - - // If any pending txs were created less than 3 min ago, use short poll time - // Otherwise use longer poll time - // (Assume no need for quick UX if user has already waited 3 min) - const threeMinutesAgo = nowSeconds() - 3 * 60 - const pollInterval = transactions.some( - tx => tx.status === TxStatus.pending && threeMinutesAgo < tx.createdAt, - ) - ? SHORT_POLL_INTERVAL_MILLISECONDS - : LONG_POLL_INTERVAL_MILLISECONDS - - console.info('WagmiTxHistoryProvider::Setting poller', pollInterval) - - const poller = setInterval(async () => { - console.info('WagmiTxHistoryProvider::poller::polling for tx updates...') - - const transactionLogs = await Promise.all( - transactions.map(txLog => { - return pollTransaction(txLog) - }), - ) - - console.info( - 'WagmiTxHistoryProvider::poller::updating transactions state', - transactionLogs, - ) - setTransactions(transactionLogs) - }, pollInterval) - - // Clean up - return () => { - console.info('WagmiTxHistoryProvider::poller::removing poller') - - clearInterval(poller) - } - }, [transactions, setTransactions]) - + const { transactions, addTransaction, removeTransaction } = useTransactions() + // TODO implement polling/wait logic using Wagmi return ( Math.round(new Date().valueOf() / 1000) export function useTransactions() { - const [transactions, setTransactionsState] = useState([]) const { userAddress, chain } = useWallet() + const [transactions, setTransactionsState] = useState([]) - const localStorageKey = - chain && userAddress - ? `transactions_${chain?.id}_${userAddress}` - : undefined + const localStorageKey = useMemo( + () => + chain && userAddress + ? `transactions_${chain?.id}_${userAddress}` + : undefined, + [chain, userAddress], + ) // Sets TransactionLogs in both localStorage and state // Ensures localStorage is always up to date, so we can persist good data on refresh @@ -49,9 +52,7 @@ export function useTransactions() { ) const removeTransaction = useCallback( - (id: number) => { - setTransactions(transactions.filter(tx => tx.id !== id)) - }, + (id: number) => setTransactions(transactions.filter(tx => tx.id !== id)), [transactions, setTransactions], ) @@ -61,14 +62,16 @@ export function useTransactions() { return } - const txs = JSON.parse(localStorage.getItem(localStorageKey) || '[]') - const pendingOrFailedTxs = txs.filter( - (tx: TransactionLog) => - tx.status !== TxStatus.success || - nowSeconds() - TX_HISTORY_TIME_SECS < tx.createdAt, - ) as TransactionLog[] - - setTransactions(pendingOrFailedTxs) + setTransactions( + JSON.parse(localStorage.getItem(localStorageKey) || '[]') + // Only persist txs that are failed/pending + // or were created within history window + .filter( + (tx: TransactionLog) => + tx.status !== TxStatus.success || + nowSeconds() - TX_HISTORY_TIME_SECS < tx.createdAt, + ) as TransactionLog[], + ) }, [setTransactions, localStorageKey]) return { diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useExportSplitsToCsv.ts b/src/hooks/useExportSplitsToCsv.ts similarity index 94% rename from src/packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useExportSplitsToCsv.ts rename to src/hooks/useExportSplitsToCsv.ts index ee14e02c38..f07098032e 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useExportSplitsToCsv.ts +++ b/src/hooks/useExportSplitsToCsv.ts @@ -1,13 +1,13 @@ import { t } from '@lingui/macro' import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext' import { BigNumber } from 'ethers' +import { Split } from 'models/splits' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' -import { Split } from 'packages/v2v3/models/splits' import { formatSplitPercent } from 'packages/v2v3/utils/math' -import { getProjectOwnerRemainderSplit } from 'packages/v2v3/utils/v2v3Splits' import { useContext, useState } from 'react' import { downloadCsvFile } from 'utils/csv' import { emitErrorNotification } from 'utils/notifications' +import { getProjectOwnerRemainderSplit } from 'utils/splits' const CSV_HEADER = [ 'beneficiary', diff --git a/src/hooks/useSubtitle.ts b/src/hooks/useSubtitle.ts index 49af09d05e..4d38cb484d 100644 --- a/src/hooks/useSubtitle.ts +++ b/src/hooks/useSubtitle.ts @@ -1,4 +1,4 @@ -import { JBProjectMetadata } from 'juice-sdk-core' +import { ProjectMetadata } from 'models/projectMetadata' import { useMemo } from 'react' import { stripHtmlTags } from 'utils/string' @@ -6,7 +6,7 @@ export type SubtitleType = 'tagline' | 'description' export const useSubtitle = ( projectMetadata: - | Pick + | Pick | undefined, ) => { const subtitle = useMemo(() => { diff --git a/src/hooks/useTransactor.ts b/src/hooks/useTransactor.ts index 801d7e66a9..7a9b97e8b9 100644 --- a/src/hooks/useTransactor.ts +++ b/src/hooks/useTransactor.ts @@ -8,7 +8,6 @@ import { CV2V3 } from 'packages/v2v3/models/cv' import { useCallback, useContext } from 'react' import { featureFlagEnabled } from 'utils/featureFlags' import { emitErrorNotification } from 'utils/notifications' -import { Hash } from 'viem' import { useWallet } from './Wallet' type TxOpts = Omit @@ -135,17 +134,10 @@ export function useTransactor(): Transactor | undefined { // add transaction to the history UI const txTitle = options?.title ?? functionName - addTransaction?.( - txTitle, - { - hash: result.hash as Hash, - timestamp: result.timestamp, - }, - { - onConfirmed: options?.onConfirmed, - onCancelled: options?.onCancelled, - }, - ) + addTransaction?.(txTitle, result as providers.TransactionResponse, { + onConfirmed: options?.onConfirmed, + onCancelled: options?.onCancelled, + }) return true } catch (e) { diff --git a/src/locales/messages.pot b/src/locales/messages.pot index 5910f43267..f89068ae21 100644 --- a/src/locales/messages.pot +++ b/src/locales/messages.pot @@ -77,9 +77,6 @@ msgstr "" msgid "per {currencyText} paid" msgstr "" -msgid "No surplus" -msgstr "" - msgid "Trust" msgstr "" @@ -209,9 +206,6 @@ msgstr "" msgid "Built by the best" msgstr "" -msgid "{0} tokens" -msgstr "" - msgid "Staked balance" msgstr "" @@ -263,9 +257,6 @@ msgstr "" msgid "Safe transactions" msgstr "" -msgid "Surplus" -msgstr "" - msgid "See example" msgstr "" @@ -368,9 +359,6 @@ msgstr "" msgid "Basic details" msgstr "" -msgid "{surplus} is available for future payouts." -msgstr "" - msgid "Your project can receive payments through the juicebox.money app." msgstr "" @@ -1049,9 +1037,6 @@ msgstr "" msgid "1. Set ENS name" msgstr "" -msgid "{surplus} is available for token redemptions or future payouts." -msgstr "" - msgid "No overflow" msgstr "" @@ -3245,9 +3230,6 @@ msgstr "" msgid "Start over" msgstr "" -msgid "<0/>fee" -msgstr "" - msgid "Juicebox provides trustless payroll capabilities to run automated payouts completely on-chain. <0>Learn more about payouts" msgstr "" @@ -3677,9 +3659,6 @@ msgstr "" msgid "(0%)" msgstr "" -msgid "Decay rate" -msgstr "" - msgid "Tokens per ETH contributed" msgstr "" @@ -4046,9 +4025,6 @@ msgstr "" msgid "Status" msgstr "" -msgid "{reservedPercent} of token issuance is set aside for the recipients below." -msgstr "" - msgid "Redemption rate:" msgstr "" @@ -4319,6 +4295,9 @@ msgstr "" msgid "Terms of Service" msgstr "" +msgid "Ruleset length" +msgstr "" + msgid "Payment notice" msgstr "" @@ -4505,9 +4484,6 @@ msgstr "" msgid "Edit handle of {projectTitle}" msgstr "" -msgid "Ruleset duration" -msgstr "" - msgid "Other assets in this project's owner's wallet." msgstr "" diff --git a/src/models/outgoingProject.ts b/src/models/outgoingProject.ts index 60c3a0e137..3e6f6abd0b 100644 --- a/src/models/outgoingProject.ts +++ b/src/models/outgoingProject.ts @@ -4,7 +4,7 @@ import { V2V3FundAccessConstraint, V2V3FundingCycle, } from 'packages/v2v3/models/fundingCycle' -import { SplitParams } from 'packages/v2v3/models/splits' +import { SplitParams } from './splits' type OutgoingGroupedSplit = { splits: SplitParams[] diff --git a/src/packages/v2v3/models/splits.ts b/src/models/splits.ts similarity index 100% rename from src/packages/v2v3/models/splits.ts rename to src/models/splits.ts diff --git a/src/models/transaction.ts b/src/models/transaction.ts index e1dd9ca9db..afb01484c3 100644 --- a/src/models/transaction.ts +++ b/src/models/transaction.ts @@ -1,4 +1,4 @@ -import { BigNumberish, Signer, Transaction } from 'ethers' +import { BigNumberish, Signer, Transaction, providers } from 'ethers' export enum TxStatus { pending = 'PENDING', @@ -20,3 +20,20 @@ export interface TransactionOptions extends TransactionCallbacks { value?: BigNumberish } +export type TransactionLog = { + id: number + title: string + createdAt: number + callbacks?: TransactionCallbacks +} & ( + | { + // Only pending txs have not been mined + status: TxStatus.pending + tx: Transaction | null + } + | { + // Once mined, tx will be a TransactionResponse + status: TxStatus.success | TxStatus.failed + tx: providers.TransactionResponse | null + } +) diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/ExportPayoutsCsvItem.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/ExportPayoutsCsvItem.tsx index 7b8dfddd5e..3a0d37113b 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/ExportPayoutsCsvItem.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/ExportPayoutsCsvItem.tsx @@ -1,7 +1,7 @@ import { ArrowUpTrayIcon } from '@heroicons/react/24/outline' import { Trans, t } from '@lingui/macro' import { Button, Tooltip } from 'antd' -import { useExportSplitsToCsv } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useExportSplitsToCsv' +import { useExportSplitsToCsv } from 'hooks/useExportSplitsToCsv' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { useContext } from 'react' import { twMerge } from 'tailwind-merge' diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/HistoricalPayoutsData.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/HistoricalPayoutsData.tsx index 12434c8112..a7b96a776f 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/HistoricalPayoutsData.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/HistoricalPayoutsData.tsx @@ -10,8 +10,8 @@ import useProjectSplits from 'packages/v2v3/hooks/contractReader/useProjectSplit import { V2V3_CURRENCY_ETH } from 'packages/v2v3/utils/currency' import { derivePayoutAmount } from 'packages/v2v3/utils/distributions' import { formatCurrencyAmount } from 'packages/v2v3/utils/formatCurrencyAmount' -import { isProjectSplit } from 'packages/v2v3/utils/v2v3Splits' import React, { useContext } from 'react' +import { isProjectSplit } from 'utils/splits' import { HistoricalConfigurationPanelProps } from './HistoricalConfigurationPanel' export const HistoricalPayoutsData: React.FC< diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationCyclesSection.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationCyclesSection.ts index c4c7522822..fc9b0d1bd1 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationCyclesSection.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationCyclesSection.ts @@ -1,6 +1,6 @@ import { t } from '@lingui/macro' +import { pairToDatum } from 'components/Project/ProjectHeader/utils/pairToDatum' import { ConfigurationPanelDatum } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' import { BigNumber } from 'ethers' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' import { V2V3FundingCycle } from 'packages/v2v3/models/fundingCycle' @@ -49,10 +49,10 @@ export const useFormatConfigurationCyclesSection = ({ const startTimeDatum: ConfigurationPanelDatum = useMemo(() => { const formattedTime = upcomingFundingCycle === null - ? formatTime(fundingCycle?.start.toNumber()) + ? formatTime(fundingCycle?.start.toBigInt()) : fundingCycle?.duration.isZero() ? t`Any time` - : formatTime(fundingCycle?.start.add(fundingCycle?.duration).toNumber()) + : formatTime(fundingCycle?.start.add(fundingCycle?.duration).toBigInt()) const formatTimeDatum: ConfigurationPanelDatum = { name: t`Start time`, diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationExtensionSection.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationExtensionSection.ts index e0f12e715b..fea2248e1d 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationExtensionSection.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationExtensionSection.ts @@ -1,14 +1,14 @@ import { t } from '@lingui/macro' +import { pairToDatum } from 'components/Project/ProjectHeader/utils/pairToDatum' import { ConfigurationPanelDatum, ConfigurationPanelTableData, } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' import { V2V3FundingCycleMetadata } from 'packages/v2v3/models/fundingCycle' import { useMemo } from 'react' import { isZeroAddress } from 'utils/address' import { etherscanLink } from 'utils/etherscan' +import { flagPairToDatum } from '../../utils/flagPairToDatum' export const useFormatConfigurationExtensionSection = ({ fundingCycleMetadata, diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.test.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.test.ts index 63edb7f0d5..ea839238a0 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.test.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.test.ts @@ -4,10 +4,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { renderHook } from '@testing-library/react-hooks' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' +import { flagPairToDatum } from '../../utils/flagPairToDatum' import { useFormatConfigurationOtherRulesSection } from './useFormatConfigurationOtherRulesSection' -jest.mock('components/Project/ProjectTabs/utils/flagPairToDatum') +jest.mock('../../utils/flagPairToDatum') describe('useFormatConfigurationOtherRulesSection', () => { const mockFundingCycleMetadata = { diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.ts index 00e07823cc..b58c216275 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationOtherRulesSection.ts @@ -1,11 +1,11 @@ import { t } from '@lingui/macro' import { - ConfigurationPanelDatum, - ConfigurationPanelTableData, + ConfigurationPanelDatum, + ConfigurationPanelTableData, } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' import { V2V3FundingCycleMetadata } from 'packages/v2v3/models/fundingCycle' import { useMemo } from 'react' +import { flagPairToDatum } from '../../utils/flagPairToDatum' export const useFormatConfigurationOtherRulesSection = ({ fundingCycleMetadata, diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.test.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.test.ts index 12150fd353..751b970813 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.test.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.test.ts @@ -3,8 +3,7 @@ */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { renderHook } from '@testing-library/react-hooks' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' +import { pairToDatum } from 'components/Project/ProjectHeader/utils/pairToDatum' import { BigNumber } from 'ethers' import { computeIssuanceRate, @@ -14,10 +13,11 @@ import { formatReservedRate, } from 'packages/v2v3/utils/math' import { formattedNum } from 'utils/format/formatNumber' +import { flagPairToDatum } from '../../utils/flagPairToDatum' import { useFormatConfigurationTokenSection } from './useFormatConfigurationTokenSection' -jest.mock('components/Project/ProjectTabs/utils/flagPairToDatum') -jest.mock('components/Project/ProjectTabs/utils/pairToDatum') +jest.mock('../../utils/flagPairToDatum') +jest.mock('components/Project/ProjectHeader/utils/pairToDatum') jest.mock('packages/v2v3/utils/math') jest.mock('utils/format/formatNumber') diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.ts index f21d20107a..c29c6b8ff0 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/useConfigurationPanel/useFormatConfigurationTokenSection.ts @@ -1,10 +1,9 @@ import { t } from '@lingui/macro' +import { pairToDatum } from 'components/Project/ProjectHeader/utils/pairToDatum' import { ConfigurationPanelDatum, ConfigurationPanelTableData, } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' import { V2V3FundingCycle, V2V3FundingCycleMetadata, @@ -19,6 +18,7 @@ import { import { useMemo } from 'react' import { formattedNum } from 'utils/format/formatNumber' import { tokenSymbolText } from 'utils/tokenSymbolText' +import { flagPairToDatum } from '../../utils/flagPairToDatum' export const useFormatConfigurationTokenSection = ({ fundingCycle, diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/usePayoutsSubPanel.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/usePayoutsSubPanel.tsx index f87ac8d8df..1fb8aa2def 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/usePayoutsSubPanel.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/hooks/usePayoutsSubPanel.tsx @@ -1,19 +1,19 @@ import { AmountInCurrency } from 'components/currency/AmountInCurrency' import { BigNumber } from 'ethers' +import { Split } from 'models/splits' import { useProjectContext } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectContext' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' -import { Split } from 'packages/v2v3/models/splits' import { V2V3CurrencyName } from 'packages/v2v3/utils/currency' import { isJuiceboxProjectSplit } from 'packages/v2v3/utils/distributions' import { MAX_DISTRIBUTION_LIMIT, SPLITS_TOTAL_PERCENT, + feeForAmount, formatSplitPercent, } from 'packages/v2v3/utils/math' -import { getProjectOwnerRemainderSplit } from 'packages/v2v3/utils/v2v3Splits' import { useCallback, useMemo } from 'react' import assert from 'utils/assert' -import { feeForAmount } from 'utils/math' +import { getProjectOwnerRemainderSplit } from 'utils/splits' import { useCurrentUpcomingDistributionLimit } from './useCurrentUpcomingDistributionLimit' import { useCurrentUpcomingPayoutSplits } from './useCurrentUpcomingPayoutSplits' import { useDistributableAmount } from './useDistributableAmount' @@ -31,7 +31,7 @@ const calculateSplitAmountWad = ( ?.mul(split.percent) .div(SPLITS_TOTAL_PERCENT) const feeAmount = splitHasFee(split) - ? feeForAmount(splitValue?.toBigInt(), primaryETHTerminalFee?.toBigInt()) ?? 0n + ? feeForAmount(splitValue, primaryETHTerminalFee) ?? BigNumber.from(0) : BigNumber.from(0) return splitValue?.sub(feeAmount) } diff --git a/src/components/Project/ProjectTabs/utils/flagPairToDatum.test.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/utils/flagPairToDatum.test.ts similarity index 100% rename from src/components/Project/ProjectTabs/utils/flagPairToDatum.test.ts rename to src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/utils/flagPairToDatum.test.ts diff --git a/src/components/Project/ProjectTabs/utils/flagPairToDatum.ts b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/utils/flagPairToDatum.ts similarity index 90% rename from src/components/Project/ProjectTabs/utils/flagPairToDatum.ts rename to src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/utils/flagPairToDatum.ts index b7efd803bc..0c6cb04da5 100644 --- a/src/components/Project/ProjectTabs/utils/flagPairToDatum.ts +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/utils/flagPairToDatum.ts @@ -1,6 +1,6 @@ import { t } from '@lingui/macro' +import { pairToDatum } from 'components/Project/ProjectHeader/utils/pairToDatum' import { ConfigurationPanelDatum } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' export const flagPairToDatum = ( name: string, diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/PreviewAddRemoveNftButton.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/PreviewAddRemoveNftButton.tsx index dd3d5a37c9..5967da2666 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/PreviewAddRemoveNftButton.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/PreviewAddRemoveNftButton.tsx @@ -1,9 +1,9 @@ import { MinusIcon, PlusIcon } from '@heroicons/react/24/solid' import { Trans, t } from '@lingui/macro' import { Button } from 'antd' -import { emitConfirmationDeletionModal } from 'hooks/emitConfirmationDeletionModal' import { useCallback } from 'react' import { twMerge } from 'tailwind-merge' +import { emitConfirmationDeletionModal } from '../../../utils/modals' const iconClasses = 'mr-1 h-6 w-6' const containerClasses = 'flex items-center justify-center' diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/RemoveNftButton.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/RemoveNftButton.tsx index b218c69098..691a70b757 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/RemoveNftButton.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/NftRewardsPanel/NftReward/RemoveNftButton.tsx @@ -1,9 +1,9 @@ import { TrashIcon } from '@heroicons/react/24/solid' import { Trans, t } from '@lingui/macro' -import { emitConfirmationDeletionModal } from 'hooks/emitConfirmationDeletionModal' import { useCallback } from 'react' import { stopPropagation } from 'react-stop-propagation' import { twMerge } from 'tailwind-merge' +import { emitConfirmationDeletionModal } from '../../../utils/modals' import { nftHoverButtonClasses } from './AddNftButton' // Button that appears when hovering an NFT reward card diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard.tsx index 09557026ec..e3e4180c0f 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/PayRedeemCard.tsx @@ -1,10 +1,10 @@ import { - ArrowDownIcon, - CheckCircleIcon, - InformationCircleIcon, - MinusIcon, - PlusIcon, - TrashIcon, + ArrowDownIcon, + CheckCircleIcon, + InformationCircleIcon, + MinusIcon, + PlusIcon, + TrashIcon, } from '@heroicons/react/24/outline' import { Trans, t } from '@lingui/macro' import { Button, Tooltip } from 'antd' @@ -16,7 +16,6 @@ import { JuiceModal, JuiceModalProps } from 'components/modals/JuiceModal' import { PV_V2 } from 'constants/pv' import { useProjectMetadataContext } from 'contexts/ProjectMetadataContext' import { useWallet } from 'hooks/Wallet' -import { emitConfirmationDeletionModal } from 'hooks/emitConfirmationDeletionModal' import { useCurrencyConverter } from 'hooks/useCurrencyConverter' import { useProjectLogoSrc } from 'hooks/useProjectLogoSrc' import { useHasNftRewards } from 'packages/v2v3/hooks/JB721Delegate/useHasNftRewards' @@ -24,8 +23,8 @@ import { useETHReceivedFromTokens } from 'packages/v2v3/hooks/contractReader/use import { useRedeemTokensTx } from 'packages/v2v3/hooks/transactor/useRedeemTokensTx' import { usePayProjectDisabled } from 'packages/v2v3/hooks/usePayProjectDisabled' import { - V2V3_CURRENCY_ETH, - V2V3_CURRENCY_USD, + V2V3_CURRENCY_ETH, + V2V3_CURRENCY_USD, } from 'packages/v2v3/utils/currency' import { formatCurrencyAmount } from 'packages/v2v3/utils/formatCurrencyAmount' import { isInfiniteDistributionLimit } from 'packages/v2v3/utils/fundingCycle' @@ -44,12 +43,13 @@ import { useTokensPanel } from '../hooks/useTokensPanel' import { useTokensPerEth } from '../hooks/useTokensPerEth' import { useUnclaimedTokenBalance } from '../hooks/useUnclaimedTokenBalance' import { - useProjectDispatch, - useProjectSelector, - useProjectStore, + useProjectDispatch, + useProjectSelector, + useProjectStore, } from '../redux/hooks' import { payRedeemActions } from '../redux/payRedeemSlice' import { projectCartActions } from '../redux/projectCartSlice' +import { emitConfirmationDeletionModal } from '../utils/modals' import { CartItemBadge } from './CartItemBadge' import { ClaimErc20Callout } from './ClaimErc20Callout' import { EthPerTokenAccordion } from './EthPerTokenAccordion' diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/TokensPanel.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/TokensPanel.tsx index 8f773394b1..1f451d969f 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/TokensPanel.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/TokensPanel.tsx @@ -3,7 +3,6 @@ import { Button } from 'antd' import EthereumAddress from 'components/EthereumAddress' import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard' import { TokenAmount } from 'components/TokenAmount' -import { AddTokenToMetamaskButton } from 'components/buttons/AddTokenToMetamaskButton' import { IssueErc20TokenButton } from 'components/buttons/IssueErc20TokenButton' import { useTokensPanel } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useTokensPanel' import { useYourBalanceMenuItems } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useYourBalanceMenuItems/useYourBalanceMenuItems' @@ -12,8 +11,8 @@ import { V2V3ClaimTokensModal } from 'packages/v2v3/components/V2V3Project/V2V3M import { V2V3MintModal } from 'packages/v2v3/components/V2V3Project/V2V3ManageTokensSection/AccountBalanceDescription/V2V3MintModal' import { useCallback, useState } from 'react' import { reloadWindow } from 'utils/windowUtils' -import { Hash } from 'viem' import { TokenHoldersModal } from '../TokenHoldersModal/TokenHoldersModal' +import { AddTokenToMetamaskButton } from './components/AddTokenToMetamaskButton' import { MigrateTokensButton } from './components/MigrateTokensButton' import { RedeemTokensButton } from './components/RedeemTokensButton' import { ReservedTokensSubPanel } from './components/ReservedTokensSubPanel' @@ -188,11 +187,8 @@ const ProjectTokenCard = () => { )}
- {projectTokenAddress && projectHasErc20Token && ( - + {projectHasErc20Token && ( + )} {canCreateErc20Token && ( diff --git a/src/components/Project/ProjectTabs/TokensPanelTooltips.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/TokensPanelTooltips.tsx similarity index 100% rename from src/components/Project/ProjectTabs/TokensPanelTooltips.tsx rename to src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/TokensPanelTooltips.tsx diff --git a/src/components/buttons/AddTokenToMetamaskButton.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/AddTokenToMetamaskButton.tsx similarity index 75% rename from src/components/buttons/AddTokenToMetamaskButton.tsx rename to src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/AddTokenToMetamaskButton.tsx index 8643167f31..4944f49247 100644 --- a/src/components/buttons/AddTokenToMetamaskButton.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/AddTokenToMetamaskButton.tsx @@ -2,9 +2,9 @@ import { Trans } from '@lingui/macro' import type { MetaMaskInpageProvider } from '@metamask/providers' import { Button } from 'antd' import { providers } from 'ethers' -import useNameOfERC20 from 'hooks/ERC20/useNameOfERC20' +import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' +import { useContext } from 'react' import { twMerge } from 'tailwind-merge' -import { Hash } from 'viem' declare global { interface Window { @@ -26,13 +26,9 @@ const useMetamask = () => { return ethereum as unknown as MetaMaskInpageProvider } -function useAddTokenToWalletRequest({ - tokenAddress, -}:{ - tokenAddress: Hash -}) { +function useAddTokenToWalletRequest() { const ethereum = useMetamask() - const { data: tokenSymbol } = useNameOfERC20(tokenAddress) + const { tokenAddress, tokenSymbol } = useContext(V2V3ProjectContext) if (!ethereum) { return @@ -53,16 +49,8 @@ function useAddTokenToWalletRequest({ } } -export function AddTokenToMetamaskButton({ - className, - tokenAddress -}: { - className: string, - tokenAddress: Hash -}) { - const addToken = useAddTokenToWalletRequest({ - tokenAddress - }) +export function AddTokenToMetamaskButton({ className }: { className: string }) { + const addToken = useAddTokenToWalletRequest() if (!addToken) return null return ( diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ExportTokensCsvItem.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ExportTokensCsvItem.tsx index 1221edb6b5..de4be5c84b 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ExportTokensCsvItem.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ExportTokensCsvItem.tsx @@ -1,6 +1,6 @@ import { ArrowUpTrayIcon } from '@heroicons/react/24/outline' import { Trans } from '@lingui/macro' -import { useExportSplitsToCsv } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useExportSplitsToCsv' +import { useExportSplitsToCsv } from 'hooks/useExportSplitsToCsv' import { useProjectContext } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectContext' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { useContext } from 'react' diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ReservedTokensSubPanel.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ReservedTokensSubPanel.tsx index edef93cc31..42a22e2173 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ReservedTokensSubPanel.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokensPanel/components/ReservedTokensSubPanel.tsx @@ -1,8 +1,8 @@ import { Trans, t } from '@lingui/macro' import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard' -import { reservedTokensTooltip } from 'components/Project/ProjectTabs/TokensPanelTooltips' import { twMerge } from 'tailwind-merge' import { ProjectAllocationRow } from '../../ProjectAllocationRow/ProjectAllocationRow' +import { reservedTokensTooltip } from '../TokensPanelTooltips' import { useReservedTokensSubPanel } from '../hooks/useReservedTokensSubPanel' import { ExportTokensCsvItem } from './ExportTokensCsvItem' import { SendReservedTokensButton } from './SendReservedTokensButton' diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx index 6cd2a4d221..265e987243 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx @@ -7,13 +7,13 @@ import EthereumAddress from 'components/EthereumAddress' import { GnosisSafeBadge } from 'components/Project/ProjectHeader/GnosisSafeBadge' import { ProjectHeaderLogo } from 'components/Project/ProjectHeader/ProjectHeaderLogo' import { ProjectHeaderPopupMenu } from 'components/Project/ProjectHeader/ProjectHeaderPopupMenu' +import { ProjectHeaderStats } from 'components/Project/ProjectHeader/ProjectHeaderStats' import { SocialLinkButton } from 'components/Project/ProjectHeader/SocialLinkButton' import { Subtitle } from 'components/Project/ProjectHeader/Subtitle' import { useSocialLinks } from 'components/Project/ProjectHeader/hooks/useSocialLinks' import { TruncatedText } from 'components/TruncatedText' import useMobile from 'hooks/useMobile' import Link from 'next/link' -import { ProjectHeaderStats } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderStats' import { useV2V3ProjectHeader } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useV2V3ProjectHeader' import V2V3ProjectHandleLink from 'packages/v2v3/components/shared/V2V3ProjectHandleLink' import { useV2V3WalletHasPermission } from 'packages/v2v3/hooks/contractReader/useV2V3WalletHasPermission' diff --git a/src/components/ExternalLinkWithIcon.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon.tsx similarity index 100% rename from src/components/ExternalLinkWithIcon.tsx rename to src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon.tsx diff --git a/src/hooks/emitConfirmationDeletionModal.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/utils/modals.tsx similarity index 93% rename from src/hooks/emitConfirmationDeletionModal.tsx rename to src/packages/v2v3/components/V2V3Project/ProjectDashboard/utils/modals.tsx index 3be15ca0d0..50c7926d22 100644 --- a/src/hooks/emitConfirmationDeletionModal.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/utils/modals.tsx @@ -3,7 +3,7 @@ import { ModalOnCancelFn, ModalOnOkFn } from 'components/modals/JuiceModal' import { LanguageProvider } from 'contexts/Language/LanguageProvider' import { ReactNode } from 'react' import { createRoot } from 'react-dom/client' -import { ConfirmationDeletionModal } from '../packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ConfirmationDeletionModal' +import { ConfirmationDeletionModal } from '../components/ui/ConfirmationDeletionModal' type EmitConfirmationDeletionModalProps = { description?: ReactNode diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig.ts b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig.ts index 35b19119a5..3a27869a11 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig.ts +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig.ts @@ -1,12 +1,12 @@ +import { + ETHPayoutGroupedSplits, + ReservedTokensGroupedSplits, +} from 'models/splits' import { V2V3FundAccessConstraint, V2V3FundingCycleData, V2V3FundingCycleMetadata, } from 'packages/v2v3/models/fundingCycle' -import { - ETHPayoutGroupedSplits, - ReservedTokensGroupedSplits, -} from 'packages/v2v3/models/splits' import { useAppSelector, useEditingV2V3FundAccessConstraintsSelector, diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts index e5a7cb07bc..75bcb2105b 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts @@ -3,6 +3,7 @@ import { RESERVED_TOKEN_SPLIT_GROUP, } from 'constants/splits' import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext' +import { Split } from 'models/splits' import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens' import { V2V3ContractsContext } from 'packages/v2v3/contexts/Contracts/V2V3ContractsContext' import { NftRewardsContext } from 'packages/v2v3/contexts/NftRewards/NftRewardsContext' @@ -10,7 +11,6 @@ import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectCo import useProjectDistributionLimit from 'packages/v2v3/hooks/contractReader/useProjectDistributionLimit' import useProjectQueuedFundingCycle from 'packages/v2v3/hooks/contractReader/useProjectQueuedFundingCycle' import useProjectSplits from 'packages/v2v3/hooks/contractReader/useProjectSplits' -import { Split } from 'packages/v2v3/models/splits' import { NO_CURRENCY, V2V3_CURRENCY_ETH } from 'packages/v2v3/utils/currency' import { SerializedV2V3FundAccessConstraint, diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCycleFormFields.ts b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCycleFormFields.ts index 3d7940503d..a614fd0cce 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCycleFormFields.ts +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCycleFormFields.ts @@ -1,6 +1,6 @@ import { DurationOption } from 'components/inputs/DurationInput' import { CurrencyName } from 'constants/currency' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { NftRewardsData } from 'redux/slices/editingV2Project/types' type DetailsSectionFields = { diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCyclePage.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCyclePage.tsx index 3b4e67276e..3c894b567d 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCyclePage.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCyclePage.tsx @@ -1,10 +1,10 @@ import { Trans } from '@lingui/macro' import { Button, Form, Tooltip } from 'antd' -import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' import Loading from 'components/Loading' import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext' import Link from 'next/link' import { useRouter } from 'next/router' +import { ExternalLinkWithIcon } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { useContext, useEffect, useRef, useState } from 'react' import { helpPagePath, settingsPagePath } from 'utils/routes' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/PayoutsSection/PayoutsSection.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/PayoutsSection/PayoutsSection.tsx index 2815f10631..d158a721d0 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/PayoutsSection/PayoutsSection.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/PayoutsSection/PayoutsSection.tsx @@ -3,8 +3,8 @@ import { Form } from 'antd' import { useWatch } from 'antd/lib/form/Form' import { JuiceSwitch } from 'components/inputs/JuiceSwitch' import { CurrencyName } from 'constants/currency' +import { Split } from 'models/splits' import { PayoutsTable } from 'packages/v2v3/components/shared/PayoutsTable/PayoutsTable' -import { Split } from 'packages/v2v3/models/splits' import { AdvancedDropdown } from '../AdvancedDropdown' import { useEditCycleFormContext } from '../EditCycleFormContext' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/usePayoutsSectionValues.ts b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/usePayoutsSectionValues.ts index 1dd15686e1..560be5887f 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/usePayoutsSectionValues.ts +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/usePayoutsSectionValues.ts @@ -1,13 +1,13 @@ import { CurrencyName } from 'constants/currency' +import { Split } from 'models/splits' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' -import { Split } from 'packages/v2v3/models/splits' import { V2V3CurrencyName } from 'packages/v2v3/utils/currency' import { distributionLimitsEqual } from 'packages/v2v3/utils/distributions' import { MAX_DISTRIBUTION_LIMIT } from 'packages/v2v3/utils/math' -import { splitsListsHaveDiff } from 'packages/v2v3/utils/v2v3Splits' import { useContext } from 'react' import { parseWad } from 'utils/format/formatNumber' +import { splitsListsHaveDiff } from 'utils/splits' import { useEditCycleFormContext } from '../../EditCycleFormContext' export const usePayoutsSectionValues = () => { diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/useTokensSectionValues.ts b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/useTokensSectionValues.ts index 3d4501932e..8a7e5c4948 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/useTokensSectionValues.ts +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/ReviewConfirmModal/hooks/useTokensSectionValues.ts @@ -9,8 +9,8 @@ import { issuanceRateFrom, reservedRateFrom, } from 'packages/v2v3/utils/math' -import { splitsListsHaveDiff } from 'packages/v2v3/utils/v2v3Splits' import { useContext } from 'react' +import { splitsListsHaveDiff } from 'utils/splits' import { tokenSymbolText } from 'utils/tokenSymbolText' import { useEditCycleFormContext } from '../../EditCycleFormContext' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/IssuanceRateReductionField.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/IssuanceRateReductionField.tsx index 9110cfd87b..5c820d05b7 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/IssuanceRateReductionField.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/IssuanceRateReductionField.tsx @@ -1,7 +1,7 @@ import { Trans } from '@lingui/macro' -import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' import { JuiceSwitch } from 'components/inputs/JuiceSwitch' import NumberSlider from 'components/inputs/NumberSlider' +import { ExternalLinkWithIcon } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon' import { useState } from 'react' import { helpPagePath } from 'utils/routes' import { useEditCycleFormContext } from '../EditCycleFormContext' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/RedemptionRateField.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/RedemptionRateField.tsx index 4fbd5367e2..6ba1e203fe 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/RedemptionRateField.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/RedemptionRateField.tsx @@ -1,9 +1,9 @@ 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 { ExternalLinkWithIcon } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon' import { useState } from 'react' import { helpPagePath } from 'utils/routes' import { useEditCycleFormContext } from '../EditCycleFormContext' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/ReservedTokensField.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/ReservedTokensField.tsx index a430826b70..9b8c237014 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/ReservedTokensField.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/ReservedTokensField.tsx @@ -1,14 +1,14 @@ 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 { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' +import { ExternalLinkWithIcon } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon' import { SPLITS_TOTAL_PERCENT } from 'packages/v2v3/utils/math' -import { totalSplitsPercent } from 'packages/v2v3/utils/v2v3Splits' import { useState } from 'react' import { helpPagePath } from 'utils/routes' +import { totalSplitsPercent } from 'utils/splits' import { V2V3EditReservedTokens } from '../../ReservedTokensSettingsPage/V2V3EditReservedTokens' import { AdvancedDropdown } from '../AdvancedDropdown' import { useEditCycleFormContext } from '../EditCycleFormContext' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/useEditCycleFormHasError.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/useEditCycleFormHasError.tsx index 52e1f5df0c..c59c80432b 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/useEditCycleFormHasError.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/useEditCycleFormHasError.tsx @@ -1,7 +1,7 @@ import { Trans } from '@lingui/macro' import { useWatch } from 'antd/lib/form/Form' import { SPLITS_TOTAL_PERCENT } from 'packages/v2v3/utils/math' -import { totalSplitsPercent } from 'packages/v2v3/utils/v2v3Splits' +import { totalSplitsPercent } from 'utils/splits' import { useEditCycleFormContext } from '../EditCycleFormContext' export function useEditCycleFormHasError() { diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/usePrepareSaveEditCycleData.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/usePrepareSaveEditCycleData.tsx index 54a83857fc..1674471a65 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/usePrepareSaveEditCycleData.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/hooks/usePrepareSaveEditCycleData.tsx @@ -1,4 +1,8 @@ import { BigNumber } from '@ethersproject/bignumber' +import { + ETHPayoutGroupedSplits, + ReservedTokensGroupedSplits, +} from 'models/splits' import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens' import { V2V3ProjectContractsContext } from 'packages/v2v3/contexts/ProjectContracts/V2V3ProjectContractsContext' import { @@ -6,10 +10,6 @@ import { V2V3FundingCycleData, V2V3FundingCycleMetadata, } from 'packages/v2v3/models/fundingCycle' -import { - ETHPayoutGroupedSplits, - ReservedTokensGroupedSplits, -} from 'packages/v2v3/models/splits' import { getV2V3CurrencyOption } from 'packages/v2v3/utils/currency' import { MAX_DISTRIBUTION_LIMIT, diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/LaunchNftCollection/hooks/useLaunchNftsForm.ts b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/LaunchNftCollection/hooks/useLaunchNftsForm.ts index d2b9d00777..c0ab706e54 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/LaunchNftCollection/hooks/useLaunchNftsForm.ts +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/LaunchNftCollection/hooks/useLaunchNftsForm.ts @@ -5,7 +5,6 @@ import { EditingFundingCycleConfig, useEditingFundingCycleConfig, } from 'packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useEditingFundingCycleConfig' -import { useReconfigureFundingCycle } from 'packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useReconfigureFundingCycle' import { useState } from 'react' import { useAppSelector } from 'redux/hooks/useAppSelector' import { @@ -18,6 +17,7 @@ import { pinNftCollectionMetadata, pinNftRewards, } from 'utils/nftRewards' +import { useReconfigureFundingCycle } from '../../../../hooks/useReconfigureFundingCycle' export const useLaunchNftsForm = () => { const [form] = useForm() diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/PayoutsSettingsPage.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/PayoutsSettingsPage.tsx index 9c21fe57e5..c827227c67 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/PayoutsSettingsPage.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/PayoutsSettingsPage.tsx @@ -1,9 +1,9 @@ import { Trans } from '@lingui/macro' import { Button } from 'antd' +import { Split } from 'models/splits' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { useSetProjectSplits } from 'packages/v2v3/hooks/transactor/useSetProjectSplitsTx' -import { Split } from 'packages/v2v3/models/splits' import { getTotalSplitsPercentage } from 'packages/v2v3/utils/distributions' import { useCallback, useContext, useMemo, useState } from 'react' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/V2V3EditPayouts.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/V2V3EditPayouts.tsx index 0e19051f9b..b024d1cf08 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/V2V3EditPayouts.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/PayoutsSettingsPage/V2V3EditPayouts.tsx @@ -1,7 +1,7 @@ import { t, Trans } from '@lingui/macro' import { CsvUpload } from 'components/inputs/CsvUpload' +import { Split } from 'models/splits' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' -import { Split } from 'packages/v2v3/models/splits' import { getTotalSplitsPercentage } from 'packages/v2v3/utils/distributions' import { useCallback, useContext, useEffect, useMemo } from 'react' @@ -18,12 +18,12 @@ import { OwnerPayoutCard } from 'packages/v2v3/components/shared/PayoutCard/Owne import { PayoutCard } from 'packages/v2v3/components/shared/PayoutCard/PayoutCard' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' import { isInfiniteDistributionLimit } from 'packages/v2v3/utils/fundingCycle' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { twMerge } from 'tailwind-merge' import { parseV2SplitsCsv } from 'utils/csv' import { formatFundingTarget } from 'utils/format/formatFundingTarget' import { formatPercent } from 'utils/format/formatPercent' import { settingsPagePath } from 'utils/routes' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' export const V2V3EditPayouts = ({ editingSplits, diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/ReservedTokensSettingsPage.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/ReservedTokensSettingsPage.tsx index ef62450bf9..bd1eebe01b 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/ReservedTokensSettingsPage.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/ReservedTokensSettingsPage.tsx @@ -1,9 +1,9 @@ import { Trans } from '@lingui/macro' import { Button } from 'antd' import { RESERVED_TOKEN_SPLIT_GROUP } from 'constants/splits' +import { Split } from 'models/splits' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { useSetProjectSplits } from 'packages/v2v3/hooks/transactor/useSetProjectSplitsTx' -import { Split } from 'packages/v2v3/models/splits' import { preciseFormatSplitPercent } from 'packages/v2v3/utils/math' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { emitErrorNotification } from 'utils/notifications' diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/V2V3EditReservedTokens.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/V2V3EditReservedTokens.tsx index 1209a124ea..5440c75aeb 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/V2V3EditReservedTokens.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/ReservedTokensSettingsPage/V2V3EditReservedTokens.tsx @@ -1,12 +1,12 @@ import { Trans } from '@lingui/macro' import { Callout } from 'components/Callout/Callout' import { CsvUpload } from 'components/inputs/CsvUpload' +import { Split } from 'models/splits' import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' import { ReservedTokensList } from 'packages/v2v3/components/shared/ReservedTokensList' -import { Split } from 'packages/v2v3/models/splits' -import { allocationToSplit, splitToAllocation } from 'packages/v2v3/utils/splitToAllocation' import { useCallback } from 'react' import { parseV2SplitsCsv } from 'utils/csv' +import { allocationToSplit, splitToAllocation } from 'utils/splitToAllocation' export function V2V3EditReservedTokens({ editingReservedTokensSplits, diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/ExportSplitsButton.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/ExportSplitsButton.tsx index 6f67f1fca1..ade123c870 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/ExportSplitsButton.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/ExportSplitsButton.tsx @@ -4,13 +4,13 @@ import { Button } from 'antd' import { ETH_PAYOUT_SPLIT_GROUP } from 'constants/splits' import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext' import { BigNumber } from 'ethers' +import { GroupedSplits, Split, SplitGroup } from 'models/splits' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' -import { GroupedSplits, Split, SplitGroup } from 'packages/v2v3/models/splits' import { formatSplitPercent } from 'packages/v2v3/utils/math' -import { getProjectOwnerRemainderSplit } from 'packages/v2v3/utils/v2v3Splits' import { PropsWithChildren, useContext, useState } from 'react' import { downloadCsvFile } from 'utils/csv' import { emitErrorNotification } from 'utils/notifications' +import { getProjectOwnerRemainderSplit } from 'utils/splits' const CSV_HEADER = [ 'beneficiary', diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/V2V3ProjectToolsDrawer.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/V2V3ProjectToolsDrawer.tsx index 1b728af9f6..05bfed3444 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/V2V3ProjectToolsDrawer.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/V2V3ProjectToolsDrawer.tsx @@ -8,11 +8,11 @@ import { } from 'constants/splits' import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext' import useMobile from 'hooks/useMobile' +import { ETHPayoutSplitGroup, ReservedTokensSplitGroup } from 'models/splits' import Link from 'next/link' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { useAddToBalanceTx } from 'packages/v2v3/hooks/transactor/AddToBalanceTx' import { useDeployProjectPayerTx } from 'packages/v2v3/hooks/transactor/useDeployProjectPayerTx' -import { ETHPayoutSplitGroup, ReservedTokensSplitGroup } from 'packages/v2v3/models/splits' import { useContext } from 'react' import { v2v3ProjectRoute } from 'utils/routes' import { ExportSplitsButton } from './ExportSplitsButton' diff --git a/src/packages/v2v3/components/shared/Allocation/AddEditAllocationModal.tsx b/src/packages/v2v3/components/shared/Allocation/AddEditAllocationModal.tsx index 66e279d275..a913db5e07 100644 --- a/src/packages/v2v3/components/shared/Allocation/AddEditAllocationModal.tsx +++ b/src/packages/v2v3/components/shared/Allocation/AddEditAllocationModal.tsx @@ -1,16 +1,14 @@ import { t, Trans } from '@lingui/macro' import { Form, Modal, Radio } from 'antd' -import { AmountPercentageInput } from 'components/Allocation/types' +import { FeeTooltipLabel } from 'components/FeeTooltipLabel' import { EthAddressInput } from 'components/inputs/EthAddressInput' import { JuiceDatePicker } from 'components/inputs/JuiceDatePicker' import { JuiceInputNumber } from 'components/inputs/JuiceInputNumber' import { LOCKED_PAYOUT_EXPLANATION } from 'components/strings' import { BigNumber } from 'ethers' import moment, * as Moment from 'moment' -import { FeeTooltipLabel } from 'packages/v2v3/components/shared/FeeTooltipLabel' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { isInfiniteDistributionLimit } from 'packages/v2v3/utils/fundingCycle' -import { projectIdToHex } from 'packages/v2v3/utils/v2v3Splits' import { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { allocationInputAlreadyExistsRule, @@ -20,10 +18,12 @@ import { } from 'utils/antdRules' import { hexToInt, parseWad, stripCommas } from 'utils/format/formatNumber' import { ceilIfCloseToNextInteger } from 'utils/math' +import { projectIdToHex } from 'utils/splits' import { Allocation } from './Allocation' import { allocationId } from './AllocationList' import { AmountInput } from './components/AmountInput' import { PercentageInput } from './components/PercentageInput' +import { AmountPercentageInput } from './types' interface AddEditAllocationModalFormProps { juiceboxProjectId?: string | undefined diff --git a/src/packages/v2v3/components/shared/Allocation/Allocation.tsx b/src/packages/v2v3/components/shared/Allocation/Allocation.tsx index f8df7277a7..1311548d88 100644 --- a/src/packages/v2v3/components/shared/Allocation/Allocation.tsx +++ b/src/packages/v2v3/components/shared/Allocation/Allocation.tsx @@ -1,9 +1,9 @@ -import { AllocationItem } from 'components/Allocation/AllocationItem' import { BigNumber } from 'ethers' import { FormItemInput } from 'models/formItemInput' +import { Split } from 'models/splits' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' -import { Split } from 'packages/v2v3/models/splits' import { createContext, useContext } from 'react' +import { AllocationItem } from './AllocationItem' import { AllocationList } from './AllocationList' import { useAllocation } from './hooks/useAllocation' diff --git a/src/components/Allocation/AllocationItem.tsx b/src/packages/v2v3/components/shared/Allocation/AllocationItem.tsx similarity index 100% rename from src/components/Allocation/AllocationItem.tsx rename to src/packages/v2v3/components/shared/Allocation/AllocationItem.tsx diff --git a/src/packages/v2v3/components/shared/Allocation/AllocationList.tsx b/src/packages/v2v3/components/shared/Allocation/AllocationList.tsx index 597c68960f..0ae3e42b60 100644 --- a/src/packages/v2v3/components/shared/Allocation/AllocationList.tsx +++ b/src/packages/v2v3/components/shared/Allocation/AllocationList.tsx @@ -5,11 +5,11 @@ import { BigNumber } from 'ethers' import { useModal } from 'hooks/useModal' import { amountFromPercent } from 'packages/v2v3/utils/distributions' import { MAX_DISTRIBUTION_LIMIT } from 'packages/v2v3/utils/math' -import { projectIdToHex } from 'packages/v2v3/utils/v2v3Splits' import { ReactNode, useCallback, useState } from 'react' import { twMerge } from 'tailwind-merge' import { fromWad, parseWad } from 'utils/format/formatNumber' import { roundIfCloseToNextInteger } from 'utils/math' +import { projectIdToHex } from 'utils/splits' import { AddEditAllocationModal, AddEditAllocationModalEntity, diff --git a/src/packages/v2v3/components/shared/Allocation/components/AllocationItemTitle.tsx b/src/packages/v2v3/components/shared/Allocation/components/AllocationItemTitle.tsx index 1603b3e43c..1ad359e934 100644 --- a/src/packages/v2v3/components/shared/Allocation/components/AllocationItemTitle.tsx +++ b/src/packages/v2v3/components/shared/Allocation/components/AllocationItemTitle.tsx @@ -3,8 +3,8 @@ import { t } from '@lingui/macro' import { Tooltip } from 'antd' import EthereumAddress from 'components/EthereumAddress' import V2V3ProjectHandleLink from 'packages/v2v3/components/shared/V2V3ProjectHandleLink' -import { isProjectSplit } from 'packages/v2v3/utils/v2v3Splits' import { formatDate } from 'utils/format/formatDate' +import { isProjectSplit } from 'utils/splits' import { AllocationSplit } from '../Allocation' export function AllocationItemTitle({ diff --git a/src/packages/v2v3/components/shared/Allocation/components/AmountInput.tsx b/src/packages/v2v3/components/shared/Allocation/components/AmountInput.tsx index d4984317e6..430b287823 100644 --- a/src/packages/v2v3/components/shared/Allocation/components/AmountInput.tsx +++ b/src/packages/v2v3/components/shared/Allocation/components/AmountInput.tsx @@ -1,4 +1,3 @@ -import { AmountPercentageInput } from 'components/Allocation/types' import CurrencySwitch from 'components/currency/CurrencySwitch' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' import { @@ -7,6 +6,7 @@ import { } from 'packages/v2v3/utils/currency' import { useCallback, useState } from 'react' import { Allocation } from '../Allocation' +import { AmountPercentageInput } from '../types' export const AmountInput = ({ value, diff --git a/src/packages/v2v3/components/shared/Allocation/components/PercentageInput.tsx b/src/packages/v2v3/components/shared/Allocation/components/PercentageInput.tsx index d144d45e6d..d92570d4ce 100644 --- a/src/packages/v2v3/components/shared/Allocation/components/PercentageInput.tsx +++ b/src/packages/v2v3/components/shared/Allocation/components/PercentageInput.tsx @@ -1,4 +1,3 @@ -import { AmountPercentageInput } from 'components/Allocation/types' import CurrencySymbol from 'components/currency/CurrencySymbol' import NumberSlider from 'components/inputs/NumberSlider' import round from 'lodash/round' @@ -7,6 +6,7 @@ import { isFiniteDistributionLimit } from 'packages/v2v3/utils/fundingCycle' import { useCallback, useMemo, useState } from 'react' import { formatWad, stripCommas } from 'utils/format/formatNumber' import { Allocation } from '../Allocation' +import { AmountPercentageInput } from '../types' export const PercentageInput = ({ value, diff --git a/src/components/Allocation/types.ts b/src/packages/v2v3/components/shared/Allocation/types.ts similarity index 100% rename from src/components/Allocation/types.ts rename to src/packages/v2v3/components/shared/Allocation/types.ts diff --git a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedJBProjectBeneficiary.tsx b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedJBProjectBeneficiary.tsx index a35b46c54d..e517bd11ed 100644 --- a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedJBProjectBeneficiary.tsx +++ b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedJBProjectBeneficiary.tsx @@ -1,5 +1,5 @@ import EthereumAddress from 'components/EthereumAddress' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { DiffedItem } from '../../DiffedItem' import { JuiceboxProjectBeneficiary } from '../../SplitItem/JuiceboxProjectBeneficiary' diff --git a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedSplitValue.tsx b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedSplitValue.tsx index d81a2d4d80..49c3723b05 100644 --- a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedSplitValue.tsx +++ b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitFields/DiffedSplitValue.tsx @@ -1,11 +1,11 @@ import { BigNumber } from 'ethers' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { isFiniteDistributionLimit, isInfiniteDistributionLimit, } from 'packages/v2v3/utils/fundingCycle' import { formatSplitPercent } from 'packages/v2v3/utils/math' -import { splitAmountsAreEqual } from 'packages/v2v3/utils/v2v3Splits' +import { splitAmountsAreEqual } from 'utils/splits' import { DiffedItem } from '../../DiffedItem' import { SplitProps } from '../../SplitItem' import { SplitAmountValue } from '../../SplitItem/SplitAmountValue' diff --git a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitItem.tsx b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitItem.tsx index 59185189ec..f5ecc06d2b 100644 --- a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitItem.tsx +++ b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitItem.tsx @@ -5,8 +5,8 @@ import { DiffPlus, } from 'packages/v2v3/components/shared/DiffedItem' import { isJuiceboxProjectSplit } from 'packages/v2v3/utils/distributions' -import { SplitWithDiff } from 'packages/v2v3/utils/v2v3Splits' import { twMerge } from 'tailwind-merge' +import { SplitWithDiff } from 'utils/splits' import { SplitProps } from '../SplitItem' import { ETHAddressBeneficiary } from '../SplitItem/EthAddressBeneficiary' import { ReservedTokensValue } from '../SplitItem/ReservedTokensValue' diff --git a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitList.tsx b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitList.tsx index ddb94ba041..3ee39b6983 100644 --- a/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitList.tsx +++ b/src/packages/v2v3/components/shared/DiffedSplits/DiffedSplitList.tsx @@ -1,13 +1,13 @@ import { BigNumber } from 'ethers' import round from 'lodash/round' +import { Split } from 'models/splits' import { useProjectContext } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectContext' -import { Split } from 'packages/v2v3/models/splits' import { formatSplitPercent } from 'packages/v2v3/utils/math' +import { useMemo } from 'react' import { getProjectOwnerRemainderSplit, processUniqueSplits, -} from 'packages/v2v3/utils/v2v3Splits' -import { useMemo } from 'react' +} from 'utils/splits' import { SplitProps } from '../SplitItem' import { DiffedSplitItem } from './DiffedSplitItem' diff --git a/src/packages/v2v3/components/shared/PayoutsTable/HeaderRows.tsx b/src/packages/v2v3/components/shared/PayoutsTable/HeaderRows.tsx index 4b3e50e921..b5c6e70a65 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/HeaderRows.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/HeaderRows.tsx @@ -1,17 +1,17 @@ import { PlusOutlined } from '@ant-design/icons' import { Trans } from '@lingui/macro' import { Button } from 'antd' -import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' +import { ExternalLinkWithIcon } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon' import { AddEditAllocationModal, AddEditAllocationModalEntity, } from 'packages/v2v3/components/shared/Allocation/AddEditAllocationModal' import { useState } from 'react' import { helpPagePath } from 'utils/routes' +import { PayoutTableSettings } from './PayoutTableSettings' +import { PayoutsTableCell } from './PayoutsTableCell' import { usePayoutsTableContext } from './context/PayoutsTableContext' import { usePayoutsTable } from './hooks/usePayoutsTable' -import { PayoutTableSettings } from './PayoutTableSettings' export function HeaderRows() { const [addRecipientModalOpen, setAddRecipientModalOpen] = useState() diff --git a/src/packages/v2v3/components/shared/PayoutsTable/PayoutSplitRow.tsx b/src/packages/v2v3/components/shared/PayoutsTable/PayoutSplitRow.tsx index be17157b54..9c7864c1e2 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/PayoutSplitRow.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/PayoutSplitRow.tsx @@ -1,15 +1,15 @@ -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' -import { PayoutsTableRow } from 'components/PayoutsTable/PayoutsTableRow' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' import round from 'lodash/round' +import { Split } from 'models/splits' import { AddEditAllocationModal, AddEditAllocationModalEntity, } from 'packages/v2v3/components/shared/Allocation/AddEditAllocationModal' -import { Split } from 'packages/v2v3/models/splits' import { useState } from 'react' import { PayoutSplitRowMenu } from './PayoutSplitRowMenu' import { PayoutTitle } from './PayoutTitle' +import { PayoutsTableCell } from './PayoutsTableCell' +import { PayoutsTableRow } from './PayoutsTableRow' import { usePayoutsTableContext } from './context/PayoutsTableContext' import { usePayoutsTable } from './hooks/usePayoutsTable' diff --git a/src/packages/v2v3/components/shared/PayoutsTable/PayoutTableSettings.tsx b/src/packages/v2v3/components/shared/PayoutsTable/PayoutTableSettings.tsx index 91f21f5d1d..cf89385e0b 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/PayoutTableSettings.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/PayoutTableSettings.tsx @@ -1,13 +1,13 @@ import { ReceiptPercentIcon, TrashIcon } from '@heroicons/react/24/outline' import { Trans } from '@lingui/macro' -import { SwitchToUnlimitedModal } from 'components/PayoutsTable/SwitchToUnlimitedModal' +import { ConvertAmountsModal } from 'components/Create/components/pages/PayoutsPage/components/ConvertAmountsModal' import { PopupMenu, PopupMenuItem } from 'components/ui/PopupMenu' -import { handleConfirmationDeletion } from 'hooks/emitConfirmationDeletionModal' -import { ConvertAmountsModal } from 'packages/v2v3/components/shared/PayoutsTable/ConvertAmountsModal' +import { handleConfirmationDeletion } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/utils/modals' import { useState } from 'react' import { ReduxDistributionLimit } from 'redux/hooks/useEditingDistributionLimit' import { fromWad } from 'utils/format/formatNumber' import { usePayoutsTable } from './hooks/usePayoutsTable' +import { SwitchToUnlimitedModal } from './modals/SwitchToUnlimitedModal' export const payoutsTableMenuItemsLabelClass = 'flex gap-2 items-center text-sm' export const payoutsTableMenuItemsIconClass = 'h-5 w-5' diff --git a/src/packages/v2v3/components/shared/PayoutsTable/PayoutTitle.tsx b/src/packages/v2v3/components/shared/PayoutsTable/PayoutTitle.tsx index 0f705ee5eb..64813d2c96 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/PayoutTitle.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/PayoutTitle.tsx @@ -2,8 +2,8 @@ import { LockFilled } from '@ant-design/icons' import { t } from '@lingui/macro' import { Tooltip } from 'antd' import EthereumAddress from 'components/EthereumAddress' +import { Split } from 'models/splits' import V2V3ProjectHandleLink from 'packages/v2v3/components/shared/V2V3ProjectHandleLink' -import { Split } from 'packages/v2v3/models/splits' import { formatDate } from 'utils/format/formatDate' import { usePayoutsTableContext } from './context/PayoutsTableContext' diff --git a/src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableBody.tsx b/src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableBody.tsx index d9a80f6916..15fb2a8c92 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableBody.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableBody.tsx @@ -1,13 +1,13 @@ import { Trans } from '@lingui/macro' import { Form } from 'antd' -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' -import { PayoutsTableRow } from 'components/PayoutsTable/PayoutsTableRow' import { Allocation } from 'packages/v2v3/components/shared/Allocation/Allocation' import { getV2V3CurrencyOption } from 'packages/v2v3/utils/currency' import { twMerge } from 'tailwind-merge' import { CurrencySwitcher } from './CurrencySwitcher' import { HeaderRows } from './HeaderRows' import { PayoutSplitRow } from './PayoutSplitRow' +import { PayoutsTableCell } from './PayoutsTableCell' +import { PayoutsTableRow } from './PayoutsTableRow' import { TotalRows } from './TotalRows' import { usePayoutsTableContext } from './context/PayoutsTableContext' import { usePayoutsTable } from './hooks/usePayoutsTable' diff --git a/src/components/PayoutsTable/PayoutsTableCell.tsx b/src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableCell.tsx similarity index 100% rename from src/components/PayoutsTable/PayoutsTableCell.tsx rename to src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableCell.tsx diff --git a/src/components/PayoutsTable/PayoutsTableRow.tsx b/src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableRow.tsx similarity index 100% rename from src/components/PayoutsTable/PayoutsTableRow.tsx rename to src/packages/v2v3/components/shared/PayoutsTable/PayoutsTableRow.tsx diff --git a/src/packages/v2v3/components/shared/PayoutsTable/TotalRows.tsx b/src/packages/v2v3/components/shared/PayoutsTable/TotalRows.tsx index e9f454053d..bf985126ee 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/TotalRows.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/TotalRows.tsx @@ -1,11 +1,11 @@ import { Trans, t } from '@lingui/macro' import { Tooltip } from 'antd' import EthereumAddress from 'components/EthereumAddress' -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' -import { PayoutsTableRow } from 'components/PayoutsTable/PayoutsTableRow' import TooltipLabel from 'components/TooltipLabel' import round from 'lodash/round' import { useProjectContext } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectContext' +import { PayoutsTableCell } from './PayoutsTableCell' +import { PayoutsTableRow } from './PayoutsTableRow' import { usePayoutsTable } from './hooks/usePayoutsTable' const Row = PayoutsTableRow diff --git a/src/packages/v2v3/components/shared/PayoutsTable/context/PayoutsTableContext.tsx b/src/packages/v2v3/components/shared/PayoutsTable/context/PayoutsTableContext.tsx index 3ee6000eba..6e03ff46ff 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/context/PayoutsTableContext.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/context/PayoutsTableContext.tsx @@ -1,5 +1,5 @@ import { CurrencyName } from 'constants/currency' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { ReactNode, createContext, useContext } from 'react' export interface PayoutsTableContextProps { diff --git a/src/packages/v2v3/components/shared/PayoutsTable/hooks/usePayoutsTable.tsx b/src/packages/v2v3/components/shared/PayoutsTable/hooks/usePayoutsTable.tsx index 801b4ddacb..a883dcc578 100644 --- a/src/packages/v2v3/components/shared/PayoutsTable/hooks/usePayoutsTable.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/hooks/usePayoutsTable.tsx @@ -2,9 +2,9 @@ import { NULL_ALLOCATOR_ADDRESS } from 'constants/contracts/mainnet/Allocators' import { ONE_BILLION, WAD_DECIMALS } from 'constants/numbers' import isEqual from 'lodash/isEqual' import round from 'lodash/round' +import { Split } from 'models/splits' import { AddEditAllocationModalEntity } from 'packages/v2v3/components/shared/Allocation/AddEditAllocationModal' import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' -import { Split } from 'packages/v2v3/models/splits' import { V2V3CurrencyName, V2V3_CURRENCY_METADATA, @@ -23,14 +23,14 @@ import { MAX_DISTRIBUTION_LIMIT, SPLITS_TOTAL_PERCENT, } from 'packages/v2v3/utils/math' +import { useMemo } from 'react' +import { parseWad } from 'utils/format/formatNumber' import { getProjectOwnerRemainderSplit, hasEqualRecipient, isProjectSplit, totalSplitsPercent, -} from 'packages/v2v3/utils/v2v3Splits' -import { useMemo } from 'react' -import { parseWad } from 'utils/format/formatNumber' +} from 'utils/splits' import { useEditCycleFormContext } from '../../../V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/EditCycleFormContext' import { usePayoutsTableContext } from '../context/PayoutsTableContext' diff --git a/src/components/PayoutsTable/SwitchToUnlimitedModal.tsx b/src/packages/v2v3/components/shared/PayoutsTable/modals/SwitchToUnlimitedModal.tsx similarity index 86% rename from src/components/PayoutsTable/SwitchToUnlimitedModal.tsx rename to src/packages/v2v3/components/shared/PayoutsTable/modals/SwitchToUnlimitedModal.tsx index c01782ee70..9ae5677d03 100644 --- a/src/components/PayoutsTable/SwitchToUnlimitedModal.tsx +++ b/src/packages/v2v3/components/shared/PayoutsTable/modals/SwitchToUnlimitedModal.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro' import { Modal } from 'antd' -import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' +import { ExternalLinkWithIcon } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/components/ui/ExternalLinkWithIcon' import { helpPagePath } from 'utils/routes' export function SwitchToUnlimitedModal({ diff --git a/src/packages/v2v3/components/shared/ReservedTokensList.tsx b/src/packages/v2v3/components/shared/ReservedTokensList.tsx index cd0a301880..61b4430ec5 100644 --- a/src/packages/v2v3/components/shared/ReservedTokensList.tsx +++ b/src/packages/v2v3/components/shared/ReservedTokensList.tsx @@ -8,10 +8,10 @@ import { } from 'packages/v2v3/components/shared/Allocation/Allocation' import { AllocationItemTitle } from 'packages/v2v3/components/shared/Allocation/components/AllocationItemTitle' import { OwnerPayoutCard } from 'packages/v2v3/components/shared/PayoutCard/OwnerPayoutCard' -import { totalSplitsPercent } from 'packages/v2v3/utils/v2v3Splits' import { useMemo } from 'react' import { formatPercent } from 'utils/format/formatPercent' import { ceilIfCloseToNextInteger } from 'utils/math' +import { totalSplitsPercent } from 'utils/splits' export const ReservedTokensList: React.FC< React.PropsWithChildren< diff --git a/src/packages/v2v3/components/shared/SplitItem/JuiceboxProjectBeneficiary.tsx b/src/packages/v2v3/components/shared/SplitItem/JuiceboxProjectBeneficiary.tsx index a98eebc246..bce98c2e78 100644 --- a/src/packages/v2v3/components/shared/SplitItem/JuiceboxProjectBeneficiary.tsx +++ b/src/packages/v2v3/components/shared/SplitItem/JuiceboxProjectBeneficiary.tsx @@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro' import { Tooltip } from 'antd' import { AllocatorBadge } from 'components/AllocatorBadge' import { NULL_ALLOCATOR_ADDRESS } from 'constants/contracts/mainnet/Allocators' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import V2V3ProjectHandleLink from '../V2V3ProjectHandleLink' export function JuiceboxProjectBeneficiary({ diff --git a/src/packages/v2v3/components/shared/SplitItem/SplitAmountValue.tsx b/src/packages/v2v3/components/shared/SplitItem/SplitAmountValue.tsx index ce6669c3b4..0959a9fbf2 100644 --- a/src/packages/v2v3/components/shared/SplitItem/SplitAmountValue.tsx +++ b/src/packages/v2v3/components/shared/SplitItem/SplitAmountValue.tsx @@ -9,10 +9,9 @@ import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectCo import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption' import { V2V3CurrencyName } from 'packages/v2v3/utils/currency' import { isJuiceboxProjectSplit } from 'packages/v2v3/utils/distributions' -import { SPLITS_TOTAL_PERCENT } from 'packages/v2v3/utils/math' +import { feeForAmount, SPLITS_TOTAL_PERCENT } from 'packages/v2v3/utils/math' import { useContext } from 'react' import { formatWad } from 'utils/format/formatNumber' -import { feeForAmount } from 'utils/math' import { SplitProps } from './SplitItem' export function SplitAmountValue({ @@ -30,10 +29,9 @@ export function SplitAmountValue({ const isJuiceboxProject = isJuiceboxProjectSplit(props.split) const hasFee = !isJuiceboxProject && !props.dontApplyFeeToAmount - const _feeAmount = hasFee - ? feeForAmount(splitValue?.toBigInt(), primaryETHTerminalFee?.toBigInt()) ?? 0n - : 0n - const feeAmount = BigNumber.from(_feeAmount) + const feeAmount = hasFee + ? feeForAmount(splitValue, primaryETHTerminalFee) ?? BigNumber.from(0) + : BigNumber.from(0) const valueAfterFees = splitValue ? splitValue.sub(feeAmount) : 0 const currencyName = V2V3CurrencyName( diff --git a/src/packages/v2v3/components/shared/SplitItem/SplitItem.tsx b/src/packages/v2v3/components/shared/SplitItem/SplitItem.tsx index 43bc15a78f..df3bd0be04 100644 --- a/src/packages/v2v3/components/shared/SplitItem/SplitItem.tsx +++ b/src/packages/v2v3/components/shared/SplitItem/SplitItem.tsx @@ -1,6 +1,6 @@ import { BigNumber } from 'ethers' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { isJuiceboxProjectSplit } from 'packages/v2v3/utils/distributions' import { ETHAddressBeneficiary } from './EthAddressBeneficiary' diff --git a/src/packages/v2v3/components/shared/SplitList.tsx b/src/packages/v2v3/components/shared/SplitList.tsx index cd94b179be..4d4d54bc6a 100644 --- a/src/packages/v2v3/components/shared/SplitList.tsx +++ b/src/packages/v2v3/components/shared/SplitList.tsx @@ -1,22 +1,9 @@ import { BigNumber } from 'ethers' -import { Split } from 'packages/v2v3/models/splits' -import { getProjectOwnerRemainderSplit, sortSplits } from 'packages/v2v3/utils/v2v3Splits' +import { Split } from 'models/splits' import { useMemo } from 'react' +import { getProjectOwnerRemainderSplit, sortSplits } from 'utils/splits' import { SplitItem, SplitProps } from './SplitItem' -export type V2V3SplitListProps = { - splits: Split[] - currency?: BigNumber - totalValue: BigNumber | undefined - projectOwnerAddress: string | undefined - showAmounts?: boolean - showFees?: boolean - valueSuffix?: string | JSX.Element - valueFormatProps?: { precision?: number } - reservedRate?: number - dontApplyFeeToAmounts?: boolean -} - export default function SplitList({ splits, showAmounts = false, @@ -28,7 +15,18 @@ export default function SplitList({ valueFormatProps, reservedRate, dontApplyFeeToAmounts, -}: V2V3SplitListProps) { +}: { + splits: Split[] + currency?: BigNumber + totalValue: BigNumber | undefined + projectOwnerAddress: string | undefined + showAmounts?: boolean + showFees?: boolean + valueSuffix?: string | JSX.Element + valueFormatProps?: { precision?: number } + reservedRate?: number + dontApplyFeeToAmounts?: boolean +}) { const ownerSplit = useMemo(() => { if (!projectOwnerAddress) return return getProjectOwnerRemainderSplit(projectOwnerAddress, splits) diff --git a/src/packages/v2v3/contexts/Project/V2V3ProjectContext.ts b/src/packages/v2v3/contexts/Project/V2V3ProjectContext.ts index 146897f735..db9719bf4c 100644 --- a/src/packages/v2v3/contexts/Project/V2V3ProjectContext.ts +++ b/src/packages/v2v3/contexts/Project/V2V3ProjectContext.ts @@ -1,10 +1,10 @@ import { BigNumber } from 'ethers' import { V2BallotState } from 'models/ballot' +import { Split } from 'models/splits' import { V2V3FundingCycle, V2V3FundingCycleMetadata, } from 'packages/v2v3/models/fundingCycle' -import { Split } from 'packages/v2v3/models/splits' import { createContext } from 'react' interface V2V3ProjectLoadingStates { diff --git a/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts b/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts index c28f7ef2e0..0f658b1d0d 100644 --- a/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts +++ b/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts @@ -15,6 +15,7 @@ import { JB_721_TIER_PARAMS_V3_2, JB_DEPLOY_TIERED_721_DELEGATE_DATA_V3_1, } from 'models/nftRewards' +import { GroupedSplits, SplitGroup } from 'models/splits' import { V2V3ContractsContext } from 'packages/v2v3/contexts/Contracts/V2V3ContractsContext' import { useJBPrices } from 'packages/v2v3/hooks/JBPrices' import { DEFAULT_JB_721_DELEGATE_VERSION } from 'packages/v2v3/hooks/defaultContracts/useDefaultJB721Delegate' @@ -28,7 +29,6 @@ import { V2V3FundAccessConstraint, V2V3FundingCycleData, } from 'packages/v2v3/models/fundingCycle' -import { GroupedSplits, SplitGroup } from 'packages/v2v3/models/splits' import { getTerminalsFromFundAccessConstraints, isValidMustStartAtOrAfter, diff --git a/src/packages/v2v3/hooks/JB721Delegate/transactor/useReconfigureV2V3FundingCycleWithNftsTx.ts b/src/packages/v2v3/hooks/JB721Delegate/transactor/useReconfigureV2V3FundingCycleWithNftsTx.ts index de53a7fbdd..f8ff95402d 100644 --- a/src/packages/v2v3/hooks/JB721Delegate/transactor/useReconfigureV2V3FundingCycleWithNftsTx.ts +++ b/src/packages/v2v3/hooks/JB721Delegate/transactor/useReconfigureV2V3FundingCycleWithNftsTx.ts @@ -8,6 +8,7 @@ import { JBDeployTiered721DelegateData, JB_DEPLOY_TIERED_721_DELEGATE_DATA_V3_1, } from 'models/nftRewards' +import { GroupedSplits, SplitGroup } from 'models/splits' import { V2V3ContractsContext } from 'packages/v2v3/contexts/Contracts/V2V3ContractsContext' import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' import { V2V3ProjectContractsContext } from 'packages/v2v3/contexts/ProjectContracts/V2V3ProjectContractsContext' @@ -20,7 +21,6 @@ import { V2V3FundAccessConstraint, V2V3FundingCycleData, } from 'packages/v2v3/models/fundingCycle' -import { GroupedSplits, SplitGroup } from 'packages/v2v3/models/splits' import { V2V3_CURRENCY_ETH } from 'packages/v2v3/utils/currency' import { isValidMustStartAtOrAfter } from 'packages/v2v3/utils/fundingCycle' import { useContext } from 'react' diff --git a/src/packages/v2v3/hooks/contractReader/useProjectSplits.ts b/src/packages/v2v3/hooks/contractReader/useProjectSplits.ts index 28430dfb00..7dae81151f 100644 --- a/src/packages/v2v3/hooks/contractReader/useProjectSplits.ts +++ b/src/packages/v2v3/hooks/contractReader/useProjectSplits.ts @@ -1,7 +1,7 @@ import { BigNumber } from 'ethers' import isEqual from 'lodash/isEqual' +import { Split, SplitGroup } from 'models/splits' import { V2V3ContractName } from 'packages/v2v3/models/contracts' -import { Split, SplitGroup } from 'packages/v2v3/models/splits' import { useCallback } from 'react' diff --git a/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts b/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts index 4a427fc026..773f825089 100644 --- a/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts +++ b/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts @@ -4,6 +4,7 @@ import { DEFAULT_MEMO } from 'constants/transactionDefaults' import { TransactionContext } from 'contexts/Transaction/TransactionContext' import { useWallet } from 'hooks/Wallet' import { TransactorInstance } from 'hooks/useTransactor' +import { GroupedSplits, SplitGroup } from 'models/splits' import { V2V3ContractsContext } from 'packages/v2v3/contexts/Contracts/V2V3ContractsContext' import { useDefaultJBController } from 'packages/v2v3/hooks/defaultContracts/useDefaultJBController' import { useDefaultJBETHPaymentTerminal } from 'packages/v2v3/hooks/defaultContracts/useDefaultJBETHPaymentTerminal' @@ -12,7 +13,6 @@ import { V2V3FundingCycleData, V2V3FundingCycleMetadata, } from 'packages/v2v3/models/fundingCycle' -import { GroupedSplits, SplitGroup } from 'packages/v2v3/models/splits' import { getTerminalsFromFundAccessConstraints, isValidMustStartAtOrAfter, diff --git a/src/packages/v2v3/hooks/transactor/useSetProjectSplitsTx.ts b/src/packages/v2v3/hooks/transactor/useSetProjectSplitsTx.ts index 6a4112002e..f9d3e8af8d 100644 --- a/src/packages/v2v3/hooks/transactor/useSetProjectSplitsTx.ts +++ b/src/packages/v2v3/hooks/transactor/useSetProjectSplitsTx.ts @@ -2,10 +2,10 @@ import { t } from '@lingui/macro' import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext' import { TransactionContext } from 'contexts/Transaction/TransactionContext' import { TransactorInstance } from 'hooks/useTransactor' +import { GroupedSplits } from 'models/splits' import { V2V3ContractsContext } from 'packages/v2v3/contexts/Contracts/V2V3ContractsContext' -import { GroupedSplits } from 'packages/v2v3/models/splits' -import { sanitizeSplit } from 'packages/v2v3/utils/v2v3Splits' import { useContext } from 'react' +import { sanitizeSplit } from 'utils/splits' import { useV2ProjectTitle } from '../useProjectTitle' export const useSetProjectSplits = ({ diff --git a/src/packages/v2v3/utils/__tests__/distributions.test.ts b/src/packages/v2v3/utils/__tests__/distributions.test.ts index 1f517a12ae..c155143ec3 100644 --- a/src/packages/v2v3/utils/__tests__/distributions.test.ts +++ b/src/packages/v2v3/utils/__tests__/distributions.test.ts @@ -1,4 +1,4 @@ -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { amountFromPercent, deriveAmountAfterFee, diff --git a/src/packages/v2v3/utils/distributions.ts b/src/packages/v2v3/utils/distributions.ts index f39a1168ec..0a31b10ead 100644 --- a/src/packages/v2v3/utils/distributions.ts +++ b/src/packages/v2v3/utils/distributions.ts @@ -1,9 +1,9 @@ import { BigNumber } from 'ethers' -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { ONE_BILLION } from 'constants/numbers' -import { isProjectSplit } from 'packages/v2v3/utils/v2v3Splits' import { fromWad, parseWad } from 'utils/format/formatNumber' +import { isProjectSplit } from 'utils/splits' import { isInfiniteDistributionLimit } from './fundingCycle' import { MAX_DISTRIBUTION_LIMIT, diff --git a/src/packages/v2v3/utils/math.ts b/src/packages/v2v3/utils/math.ts index 0eda137b51..c029855f97 100644 --- a/src/packages/v2v3/utils/math.ts +++ b/src/packages/v2v3/utils/math.ts @@ -20,7 +20,7 @@ import { fromWad, percentToPermyriad, } from 'utils/format/formatNumber' -import { feeForAmount, WeightFunction } from 'utils/math' +import { WeightFunction } from 'utils/math' export const MAX_RESERVED_RATE = TEN_THOUSAND export const MAX_REDEMPTION_RATE = TEN_THOUSAND @@ -233,12 +233,20 @@ export const weightAmountPermyriad: WeightFunction = ( ) } +export const feeForAmount = ( + amountWad: BigNumber | undefined, + feePerBillion: BigNumber | undefined, +): BigNumber | undefined => { + if (!feePerBillion || !amountWad) return + return amountWad.mul(feePerBillion).div(ONE_BILLION) +} + export const amountSubFee = ( amountWad: BigNumber | undefined, feePerBillion: BigNumber | undefined, ): BigNumber | undefined => { if (!feePerBillion || !amountWad) return - const feeAmount = feeForAmount(amountWad.toBigInt(), feePerBillion.toBigInt()) ?? 0 + const feeAmount = feeForAmount(amountWad, feePerBillion) ?? 0 return amountWad.sub(feeAmount) } @@ -247,7 +255,7 @@ export const amountSubFee = ( // and return the sum of them all export function sumHeldFees(fees: JBFee[]) { return fees.reduce((sum, heldFee) => { - const amountWad = feeForAmount(heldFee.amount.toBigInt(), BigInt(heldFee.fee)) + const amountWad = feeForAmount(heldFee.amount, BigNumber.from(heldFee.fee)) const amountNum = parseFloat(fromWad(amountWad)) return sum + (amountNum ?? 0) }, 0) diff --git a/src/packages/v4/components/ActivityList/ActivitiyList.tsx b/src/packages/v4/components/ActivityList/ActivitiyList.tsx new file mode 100644 index 0000000000..9c33a90ed9 --- /dev/null +++ b/src/packages/v4/components/ActivityList/ActivitiyList.tsx @@ -0,0 +1,95 @@ +import EthereumAddress from 'components/EthereumAddress' +import EtherscanLink from 'components/EtherscanLink' +import { formatDistance } from 'date-fns' +import { Ether, JBProjectToken } from 'juice-sdk-core' +import { useJBContractContext, useJBTokenContext } from 'juice-sdk-react' +import { + OrderDirection, + PayEvent_OrderBy, + PayEventsDocument, + PayEventsQuery, +} from 'packages/v4/graphql/client/graphql' +import { useSubgraphQuery } from 'packages/v4/graphql/useSubgraphQuery' +import { Address } from 'viem' + +type PayEvent = { + id: string + amount: Ether + beneficiary: Address + beneficiaryTokenCount?: JBProjectToken + timestamp: number + txHash: string +} + +function ActivityItem(ev: PayEvent) { + const { token } = useJBTokenContext() + if (!token?.data) return null + + const formattedDate = formatDistance(ev.timestamp * 1000, new Date(), { + addSuffix: true, + }) + + return ( +
+
+ +
+ bought {ev.beneficiaryTokenCount?.format(6)} {token.data.symbol} +
+
+
+ Paid {ev.amount.format(6)} ETH •{' '} + + {formattedDate} + +
+
+ ) +} + +function transformPayEventsRes( + data: PayEventsQuery | undefined, +): PayEvent[] | undefined { + return data?.payEvents.map(event => { + return { + id: event.id, + amount: new Ether(BigInt(event.amount)), + beneficiary: event.beneficiary, + beneficiaryTokenCount: new JBProjectToken( + // BigInt(0) + BigInt(event.beneficiaryTokenCount), + ), + timestamp: event.timestamp, + txHash: event.txHash, + } + }) +} + +export function ActivityList() { + const { projectId } = useJBContractContext() + const { data } = useSubgraphQuery(PayEventsDocument, { + orderBy: PayEvent_OrderBy.timestamp, + orderDirection: OrderDirection.desc, + where: { + // pv: PV2, + projectId: Number(projectId), + }, + }) + + const payEvents = transformPayEventsRes(data) + + return ( +
+
Activity
+
+ {payEvents && payEvents.length > 0 ? ( + payEvents?.map(event => { + return + }) + ) : ( + No activity yet. + )} +
+
+ ) +} diff --git a/src/packages/v4/components/Allocation/AddEditAllocationModal.tsx b/src/packages/v4/components/Allocation/AddEditAllocationModal.tsx deleted file mode 100644 index 05ec3ccd79..0000000000 --- a/src/packages/v4/components/Allocation/AddEditAllocationModal.tsx +++ /dev/null @@ -1,373 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Form, Modal, Radio } from 'antd' -import { AmountPercentageInput } from 'components/Allocation/types' -import { EthAddressInput } from 'components/inputs/EthAddressInput' -import { JuiceDatePicker } from 'components/inputs/JuiceDatePicker' -import { JuiceInputNumber } from 'components/inputs/JuiceInputNumber' -import { LOCKED_PAYOUT_EXPLANATION } from 'components/strings' -import { BigNumber } from 'ethers' -import { useReadJbMultiTerminalFee } from 'juice-sdk-react' -import moment, * as Moment from 'moment' -import { isInfinitePayoutLimit } from 'packages/v4/utils/fundingCycle' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { - allocationInputAlreadyExistsRule, - inputIsIntegerRule, - inputMustBeEthAddressRule, - inputMustExistRule, -} from 'utils/antdRules' -import { hexToInt, stripCommas } from 'utils/format/formatNumber' -import { ceilIfCloseToNextInteger } from 'utils/math' -import { Hash } from 'viem' -import { FeeTooltipLabel } from '../FeeTooltipLabel' -import { Allocation } from './Allocation' -import { AmountInput } from './components/AmountInput' -import { PercentageInput } from './components/PercentageInput' - - -export const allocationId = ( - beneficiary: string, - projectId: string | undefined, -) => { - const hasProjectId = Boolean(projectId && projectId !== '0') - return `${beneficiary}${hasProjectId ? `-${projectId}` : ''}` -} - -interface AddEditAllocationModalFormProps { - juiceboxProjectId?: string | undefined - address?: Hash | undefined - amount?: AmountPercentageInput | undefined - lockedUntil?: moment.Moment | null | undefined -} - -export type AddEditAllocationModalEntity = - | { - projectOwner: false - beneficiary: Hash | undefined - projectId: string | undefined - amount: AmountPercentageInput - lockedUntil: number | undefined - previousId?: string - } - | { - projectOwner: true - amount: string - } - -export const AddEditAllocationModal = ({ - className, - allocationName, - editingData, - availableModes, - open, - onOk, - onCancel, - hideProjectOwnerOption, - hideFee, -}: { - className?: string - allocationName: string - editingData?: AddEditAllocationModalEntity | undefined - availableModes: Set<'amount' | 'percentage'> - open?: boolean - onOk: (split: AddEditAllocationModalEntity) => void - onCancel: VoidFunction - hideProjectOwnerOption?: boolean - hideFee?: boolean -}) => { - const { data: primaryNativeTerminalFee } = useReadJbMultiTerminalFee() - - const { totalAllocationAmount, allocations, allocationCurrency } = - Allocation.useAllocationInstance() - const [form] = Form.useForm() - const [amountType, setAmountType] = useState<'amount' | 'percentage'>() - const [recipient, setRecipient] = useState< - 'walletAddress' | 'juiceboxProject' | 'projectOwner' - >('walletAddress') - - const amount = Form.useWatch('amount', form) - - const showFee = - amountType === 'amount' && recipient === 'walletAddress' && !hideFee - - const isValidJuiceboxProject = useMemo( - () => - !editingData?.projectOwner && - editingData?.projectId && - editingData.projectId !== BigNumber.from(0).toHexString(), - [editingData], - ) - - const totalAllocationPercent = allocations - .map(a => a.percent) - .reduce((acc, curr) => acc + curr.toFloat(), 0) - - const hasInfiniteTotalAllocationAmount: boolean = useMemo( - () => - Boolean( - totalAllocationAmount && - isInfinitePayoutLimit(totalAllocationAmount), - ), - [totalAllocationAmount], - ) - - const isEditing = !!editingData - - useEffect(() => { - setAmountType(() => { - if (availableModes.has('amount') && !hasInfiniteTotalAllocationAmount) { - return 'amount' - } - return 'percentage' - }) - }, [availableModes, hasInfiniteTotalAllocationAmount]) - - useEffect(() => { - if (!open) return - - if (!editingData) { - setRecipient('walletAddress') - return - } - - if (editingData.projectOwner) { - setRecipient('projectOwner') - return - } - - setRecipient(isValidJuiceboxProject ? 'juiceboxProject' : 'walletAddress') - - setTimeout(() => { - form.setFieldsValue({ - juiceboxProjectId: isValidJuiceboxProject - ? hexToInt(editingData?.projectId).toString() - : undefined, - address: editingData.beneficiary, - amount: editingData.amount, - lockedUntil: editingData.lockedUntil - ? Moment.default(editingData.lockedUntil * 1000) - : undefined, - }) - }, 0) - }, [editingData, form, open, totalAllocationAmount, isValidJuiceboxProject]) - - const onModalOk = useCallback(async () => { - const fields = await form.validateFields() - if (!fields.amount) throw new Error('Missing amount') - let result: AddEditAllocationModalEntity - if (recipient === 'projectOwner') { - result = { projectOwner: true, amount: fields.amount.value } - } else { - const hasEditingBeneficiary = editingData && !editingData?.projectOwner - result = { - projectOwner: false, - beneficiary: fields.address, - projectId: fields.juiceboxProjectId, - amount: fields.amount, - lockedUntil: fields.lockedUntil - ? Math.round(fields.lockedUntil.valueOf() / 1000) - : undefined, - previousId: hasEditingBeneficiary - ? allocationId( - editingData?.beneficiary ?? '', - fields.juiceboxProjectId, - ) - : undefined, - } - } - onOk(result) - form.resetFields() - }, [form, onOk, recipient, editingData]) - - const onModalCancel = useCallback(() => { - onCancel() - form.resetFields() - }, [form, onCancel]) - - const addressLabel = - recipient === 'juiceboxProject' - ? t`Project token beneficiary address` - : t`Address` - const addressExtra = - recipient === 'juiceboxProject' ? ( - - Paying another Juicebox project may mint its tokens. Select an address - to receive these tokens. - - ) : undefined - - const showProjectOwnerRecipientOption = - amountType !== 'percentage' && - (!allocations.length || - ceilIfCloseToNextInteger(totalAllocationPercent) === 100) && - !hideProjectOwnerOption - - const projectId = Form.useWatch('juiceboxProjectId', form) - - const titleCasedAllocationName = useMemo( - () => - allocationName - .toLowerCase() - .split(' ') - .map(s => s.charAt(0).toUpperCase() + s.slice(1), '') - .join(' '), - [allocationName], - ) - - if (availableModes.size === 0) { - console.error('AddEditAllocationModal: no available modes') - return null - } - - return ( - - {isEditing ? t`Edit ${allocationName}` : t`Add new ${allocationName}`} - - } - okText={isEditing ? t`Save ${allocationName}` : t`Add ${allocationName}`} - open={open} - onOk={onModalOk} - onCancel={onModalCancel} - destroyOnClose - > -
- {availableModes.size > 1 && ( - setAmountType(e.target.value)} - > - - Amounts - - - Percentages - - - )} - - setRecipient(e.target.value)} - > - - Wallet Address - - - Juicebox Project - - {showProjectOwnerRecipientOption && ( - - Project Owner - - )} - - - - {recipient === 'juiceboxProject' && ( - - - - )} - {recipient !== 'projectOwner' && ( - ({ - beneficiary, - projectId: projectId?.toString(), - })) - .filter( - ( - a, - ): a is { - beneficiary: Hash - projectId: string - } => !!a.beneficiary, - ), - inputProjectId: projectId, - editingAddressBeneficiary: !editingData?.projectOwner - ? editingData?.beneficiary - : undefined, - }), - ]} - > - - - )} - - - ) - } - rules={[ - inputMustExistRule({ - label: - amountType === 'amount' - ? t`${titleCasedAllocationName} Amount` - : t`${titleCasedAllocationName} Percentage`, - }), - ]} - > - {amountType === 'percentage' ? : } - - {recipient !== 'projectOwner' && ( - - current < moment().endOf('day')} - /> - - )} -
-
- ) -} diff --git a/src/packages/v4/components/Allocation/Allocation.tsx b/src/packages/v4/components/Allocation/Allocation.tsx deleted file mode 100644 index 243ba8b1a0..0000000000 --- a/src/packages/v4/components/Allocation/Allocation.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { JBSplit as Split } from 'juice-sdk-core' -import { FormItemInput } from 'models/formItemInput' -import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption' -import { createContext, useContext } from 'react' -import { AllocationItem } from './AllocationItem' -import { useAllocation } from './hooks/useAllocation' - -export type AllocationSplit = Split & { id: string } - -const DEFAULT_SET_CURRENCY_FN = () => { - console.error('AllocationContext.setCurrency called but no provider set') -} - -const AllocationContext = createContext<{ - allocations: AllocationSplit[] - totalAllocationAmount?: bigint - setTotalAllocationAmount?: (total: bigint) => void - allocationCurrency?: V4CurrencyOption - addAllocation: (allocation: AllocationSplit) => void - removeAllocation: (id: string) => void - upsertAllocation: (allocation: AllocationSplit) => void - setAllocations: (allocations: AllocationSplit[]) => void - setCurrency: (currency: V4CurrencyOption) => void -}>({ - allocations: [], - addAllocation: () => { - console.error('AllocationContext.addAllocation called but no provider set') - }, - removeAllocation: () => { - console.error( - 'AllocationContext.removeAllocation called but no provider set', - ) - }, - upsertAllocation: () => { - console.error( - 'AllocationContext.upsertAllocation called but no provider set', - ) - }, - setAllocations: () => { - console.error('AllocationContext.setAllocations called but no provider set') - }, - setCurrency: DEFAULT_SET_CURRENCY_FN, -}) - -const useAllocationInstance = () => { - return useContext(AllocationContext) -} - -interface AllocationProps { - totalAllocationAmount?: bigint - setTotalAllocationAmount?: (total: bigint) => void - allocationCurrency?: V4CurrencyOption - setAllocationCurrency?: (currency: V4CurrencyOption) => void -} - -export const Allocation: React.FC< - React.PropsWithChildren> -> & { - Item: typeof AllocationItem - useAllocationInstance: typeof useAllocationInstance -} = ({ - totalAllocationAmount, - setTotalAllocationAmount, - allocationCurrency, - value, - onChange, - setAllocationCurrency, - children, -}) => { - const allocationHook = useAllocation({ value, onChange }) - - return ( - - {children} - - ) -} - -Allocation.useAllocationInstance = useAllocationInstance -Allocation.Item = AllocationItem diff --git a/src/packages/v4/components/Allocation/AllocationItem.tsx b/src/packages/v4/components/Allocation/AllocationItem.tsx deleted file mode 100644 index 267e5be548..0000000000 --- a/src/packages/v4/components/Allocation/AllocationItem.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { ReactNode } from 'react' -import { twMerge } from 'tailwind-merge' - -export const AllocationItem = ({ - className, - title, - amount, - extra, - onClick, -}: { - className?: string - title: ReactNode - amount: ReactNode - extra?: ReactNode - onClick?: VoidFunction -}) => { - const isClickable = !!onClick - - return ( -
-
{title}
-
- {extra} -
-
- {amount} -
-
- ) -} diff --git a/src/packages/v4/components/Allocation/components/AmountInput.tsx b/src/packages/v4/components/Allocation/components/AmountInput.tsx deleted file mode 100644 index 128f251760..0000000000 --- a/src/packages/v4/components/Allocation/components/AmountInput.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import CurrencySwitch from 'components/currency/CurrencySwitch' -import FormattedNumberInput from 'components/inputs/FormattedNumberInput' - -import { AmountPercentageInput } from 'components/Allocation/types' -import { V4_CURRENCY_ETH, V4_CURRENCY_USD } from 'packages/v4/utils/currency' -import { useCallback, useState } from 'react' -import { Allocation } from '../Allocation' - -export const AmountInput = ({ - value, - onChange, -}: { - value?: AmountPercentageInput - onChange?: (input: AmountPercentageInput | undefined) => void -}) => { - const [_amount, _setAmount] = useState({ value: '' }) - const amount = value ?? _amount - const setAmount = onChange ?? _setAmount - - const { allocationCurrency, setCurrency } = Allocation.useAllocationInstance() - const currency = allocationCurrency ?? V4_CURRENCY_ETH - - const onAmountInputChange = useCallback( - (amount: AmountPercentageInput | undefined) => { - if (amount && !isNaN(parseFloat(amount.value))) { - setAmount(amount) - return - } - }, - [setAmount], - ) - - return ( -
- onAmountInputChange(val ? { value: val } : undefined)} - accessory={ - - setCurrency(c === 'ETH' ? V4_CURRENCY_ETH : V4_CURRENCY_USD) - } - className="rounded" - /> - } - /> -
- ) -} diff --git a/src/packages/v4/components/Allocation/components/PercentageInput.tsx b/src/packages/v4/components/Allocation/components/PercentageInput.tsx deleted file mode 100644 index de00133742..0000000000 --- a/src/packages/v4/components/Allocation/components/PercentageInput.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { AmountPercentageInput } from 'components/Allocation/types' -import CurrencySymbol from 'components/currency/CurrencySymbol' -import NumberSlider from 'components/inputs/NumberSlider' -import round from 'lodash/round' -import { V4CurrencyName } from 'packages/v4/utils/currency' -import { isFinitePayoutLimit } from 'packages/v4/utils/fundingCycle' -import { useCallback, useMemo, useState } from 'react' -import { formatWad, stripCommas } from 'utils/format/formatNumber' -import { Allocation } from '../Allocation' - -export const PercentageInput = ({ - value, - onChange, -}: { - value?: AmountPercentageInput - onChange?: (input: AmountPercentageInput | undefined) => void -}) => { - const [_percentage, _setPercentage] = useState< - AmountPercentageInput | undefined - >({ - value: '', - isPercent: true, - }) - - const { totalAllocationAmount, allocationCurrency } = - Allocation.useAllocationInstance() - - const hasTotalAllocationAmount = useMemo( - () => isFinitePayoutLimit(totalAllocationAmount), - [totalAllocationAmount], - ) - - const percentage = value ?? _percentage - const setPercentage = onChange ?? _setPercentage - - const onAmountInputChange = useCallback( - (percentage: AmountPercentageInput | undefined) => { - setPercentage(percentage) - return - }, - [setPercentage], - ) - - const totalAllocationAmountNum = parseFloat( - stripCommas(formatWad(totalAllocationAmount) ?? '0'), - ) - const currencyName = V4CurrencyName(allocationCurrency) - const roundedAmount = round( - (percentage ? parseFloat(percentage.value) / 100 : 0) * - totalAllocationAmountNum, - currencyName === 'ETH' ? 4 : 2, - ) - return ( -
-
- - onAmountInputChange({ - value: percentage?.toString() ?? '', - isPercent: true, - }) - } - step={0.01} - defaultValue={0} - suffix="%" - /> -
- {/* Read-only amount if distribution limit is not infinite */} - {hasTotalAllocationAmount ? ( -
- - {roundedAmount} -
- ) : null} -
- ) -} diff --git a/src/packages/v4/components/Allocation/hooks/useAllocation.ts b/src/packages/v4/components/Allocation/hooks/useAllocation.ts deleted file mode 100644 index f1ab54e763..0000000000 --- a/src/packages/v4/components/Allocation/hooks/useAllocation.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useArray } from 'hooks/useArray' -import { FormItemInput } from 'models/formItemInput' -import { AllocationSplit } from '../Allocation' - -export const useAllocation = ({ - value, - onChange, -}: FormItemInput) => { - const { - values: allocations, - add: addAllocation, - remove: removeAllocation, - upsert: upsertAllocation, - set: setAllocations, - } = useArray([value, onChange]) - - return { - allocations, - addAllocation, - removeAllocation, - upsertAllocation, - setAllocations, - } -} diff --git a/src/packages/v4/components/FeeTooltipLabel.tsx b/src/packages/v4/components/FeeTooltipLabel.tsx deleted file mode 100644 index 317edc5d1f..0000000000 --- a/src/packages/v4/components/FeeTooltipLabel.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Trans } from '@lingui/macro' -import ExternalLink from 'components/ExternalLink' -import TooltipLabel from 'components/TooltipLabel' -import CurrencySymbol from 'components/currency/CurrencySymbol' -import { Ether } from 'juice-sdk-core' -import { NativeTokenValue } from 'juice-sdk-react' -import { formatWad } from 'utils/format/formatNumber' -import { helpPagePath } from 'utils/routes' -import { V4CurrencyOption } from '../models/v4CurrencyOption' -import { V4_CURRENCY_ETH } from '../utils/currency' -import { amountSubFee } from '../utils/math' - -export const FeeTooltipLabel = ({ - currency, - amount, - feePerBillion, -}: { - currency: V4CurrencyOption - amount: bigint | undefined - feePerBillion: bigint | undefined -}) => { - if (!amount || !currency || !feePerBillion) return null - const amountSubFeeValue = amountSubFee(amount, feePerBillion) ?? 0n - const feePercentage = new Ether(feePerBillion).format() - return ( - - {currency === V4_CURRENCY_ETH ? ( - - ) : ( - <> - - {formatWad(amountSubFeeValue, { precision: 4 })} - - )}{' '} - after {feePercentage}% JBX membership fee - - } - tip={ - - Payouts to Ethereum addresses incur a {feePercentage}% fee. Your - project will receive JBX in return.{' '} - - Learn more - - . - - } - /> - ) -} diff --git a/src/packages/v4/components/PayoutsTable/ConvertAmountsModal.tsx b/src/packages/v4/components/PayoutsTable/ConvertAmountsModal.tsx deleted file mode 100644 index e7ed6b0a6a..0000000000 --- a/src/packages/v4/components/PayoutsTable/ConvertAmountsModal.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { t, Trans } from '@lingui/macro' -import { Divider, Modal } from 'antd' -import CurrencySwitch from 'components/currency/CurrencySwitch' -import EthereumAddress from 'components/EthereumAddress' -import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' -import FormattedNumberInput from 'components/inputs/FormattedNumberInput' -import { Parenthesis } from 'components/Parenthesis' -import { JBSplit as Split, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' -import { useRouter } from 'next/router' - -import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption' -import { V4_CURRENCY_ETH, V4_CURRENCY_USD } from 'packages/v4/utils/currency' -import { - deriveAmountAfterFee, - derivePayoutAmount, -} from 'packages/v4/utils/distributions' -import { formatCurrencyAmount } from 'packages/v4/utils/formatCurrencyAmount' -import { allocationToSplit, splitToAllocation } from 'packages/v4/utils/splitToAllocation' -import { isJuiceboxProjectSplit } from 'packages/v4/utils/v4Splits' -import { ReactNode, useCallback, useMemo, useState } from 'react' -import { - ReduxDistributionLimit, - useEditingDistributionLimit, -} from 'redux/hooks/useEditingDistributionLimit' -import { parseWad } from 'utils/format/formatNumber' -import { formatPercent } from 'utils/format/formatPercent' -import { helpPagePath } from 'utils/routes' -import V4ProjectHandleLink from '../V4ProjectHandleLink' - -export const ConvertAmountsModal = ({ - open, - onOk, - onCancel, - splits, -}: { - open: boolean - onOk: (d: ReduxDistributionLimit) => void - onCancel: VoidFunction - splits: Split[] -}) => { - const [distributionLimit] = useEditingDistributionLimit() - const [newDistributionLimit, setNewDistributionLimit] = useState('') - const [currency, setCurrency] = useState( - distributionLimit?.currency ?? V4_CURRENCY_ETH, - ) - - const router = useRouter() - const { chainName } = router.query - - const totalPayoutsPercent = useMemo( - () => - splits - .map(s => (s.percent.toFloat() / SPLITS_TOTAL_PERCENT) * 100) - .reduce((acc, curr) => acc + curr, 0), - [splits], - ) - - const ownerPercent = useMemo( - () => Math.max(0, 100 - totalPayoutsPercent), - [totalPayoutsPercent], - ) - - const onModalOk = useCallback(() => { - onOk({ - amount: parseWad(parseFloat(newDistributionLimit)), - currency, - }) - }, [currency, newDistributionLimit, onOk]) - - const hasOwnerPayout = ownerPercent > 0 - - const okButtonDisabled = !newDistributionLimit.length - - return ( - - Switch to limited payouts - - } - open={open} - okButtonProps={{ disabled: okButtonDisabled }} - onOk={onModalOk} - onCancel={onCancel} - okText={t`Convert to amounts`} - > -
- - To switch to 'Limited' payouts, enter a total amount to pay out of the - treasury to split between your recipients. - {' '} - - Learn more about payout limits - -
- - -
- setNewDistributionLimit(val ? val : '')} - accessory={ - - setCurrency(c === 'ETH' ? V4_CURRENCY_ETH : V4_CURRENCY_USD) - } - /> - } - /> -
- -
- - Current payouts - - {hasOwnerPayout ? ( - - {newDistributionLimit && - formatCurrencyAmount({ - amount: deriveAmountAfterFee( - (ownerPercent / 100) * parseFloat(newDistributionLimit), - ), - currency, - })}{' '} - {formatPercent(ownerPercent)} - - } - /> - ) : null} - {splits.map(split => { - const allocation = splitToAllocation(split) - return ( - - {isJuiceboxProjectSplit(split) && allocation.projectId ? ( - - ) : ( - - )} - - } - amountLabel={ - <> - {newDistributionLimit && - formatCurrencyAmount({ - amount: derivePayoutAmount({ - payoutSplit: allocationToSplit(allocation), - distributionLimit: parseFloat(newDistributionLimit), - }), - currency, - })}{' '} - {allocation.percent.formatPercentage()}% - - } - /> - ) - })} -
-
- ) -} - -const PayoutChangeSplitInfo = ({ - descriptionLabel, - amountLabel, -}: { - descriptionLabel: ReactNode - amountLabel: ReactNode -}) => { - return ( - <> - -
- {descriptionLabel} - {amountLabel} -
- - ) -} diff --git a/src/packages/v4/components/PayoutsTable/CurrencySwitcher.tsx b/src/packages/v4/components/PayoutsTable/CurrencySwitcher.tsx deleted file mode 100644 index 44b3c643c6..0000000000 --- a/src/packages/v4/components/PayoutsTable/CurrencySwitcher.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { ArrowPathIcon, ChevronDownIcon } from '@heroicons/react/24/outline' -import { Trans } from '@lingui/macro' -import { PopupMenu } from 'components/ui/PopupMenu' -import { useCurrencyConverter } from 'hooks/useCurrencyConverter' -import round from 'lodash/round' - -import { V4_CURRENCY_ETH, V4_CURRENCY_USD } from 'packages/v4/utils/currency' -import { fromWad, parseWad } from 'utils/format/formatNumber' -import { - payoutsTableMenuItemsIconClass, - payoutsTableMenuItemsLabelClass, -} from './PayoutTableSettings' -import { usePayoutsTableContext } from './context/PayoutsTableContext' -import { usePayoutsTable } from './hooks/usePayoutsTable' - -export function CurrencySwitcher() { - const { setCurrency: setCurrencyName } = usePayoutsTableContext() - const { currency, setCurrency, distributionLimit, setDistributionLimit } = - usePayoutsTable() - const converter = useCurrencyConverter() - - const button = ( -
- {currency === 'ETH' ? ( - Amount (ETH) - ) : ( - Amount (USD) - )} - {setCurrencyName ? : null} -
- ) - - if (!setCurrencyName) { - return button - } - - const itemsClassName = `${payoutsTableMenuItemsLabelClass} text-primary` - const items = - currency === 'ETH' - ? [ - { - id: 'switchToUsd', - label: ( -
- - Convert to USD -
- ), - onClick: () => { - const usdAmount = converter.wadToCurrency( - parseWad(distributionLimit), - 'USD', - 'ETH', - ) - const formattedUsdAmount = round( - parseFloat(fromWad(usdAmount)), - 2, - ) - setDistributionLimit(formattedUsdAmount) - setCurrency(V4_CURRENCY_USD) - }, - }, - ] - : [ - { - id: 'switchToEth', - label: ( -
- - Convert to ETH -
- ), - onClick: () => { - const ethAmount = converter.wadToCurrency( - parseWad(distributionLimit), - 'ETH', - 'USD', - ) - const formattedEthAmount = round( - parseFloat(fromWad(ethAmount)), - 4, - ) - setDistributionLimit(formattedEthAmount) - setCurrency(V4_CURRENCY_ETH) - }, - }, - ] - - return -} diff --git a/src/packages/v4/components/PayoutsTable/HeaderRows.tsx b/src/packages/v4/components/PayoutsTable/HeaderRows.tsx deleted file mode 100644 index fc03affe96..0000000000 --- a/src/packages/v4/components/PayoutsTable/HeaderRows.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons' -import { Trans } from '@lingui/macro' -import { Button } from 'antd' -import { ExternalLinkWithIcon } from 'components/ExternalLinkWithIcon' - -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' -import { useState } from 'react' -import { helpPagePath } from 'utils/routes' -import { AddEditAllocationModal, AddEditAllocationModalEntity } from '../Allocation/AddEditAllocationModal' -import { usePayoutsTableContext } from './context/PayoutsTableContext' -import { usePayoutsTable } from './hooks/usePayoutsTable' -import { PayoutTableSettings } from './PayoutTableSettings' - -export function HeaderRows() { - const [addRecipientModalOpen, setAddRecipientModalOpen] = useState() - - const { hideExplaination, hideSettings, addPayoutsDisabled } = - usePayoutsTableContext() - - const { distributionLimitIsInfinite, handleNewPayoutSplit } = - usePayoutsTable() - - const handleAddRecipientModalOk = ( - newSplit: AddEditAllocationModalEntity, - ) => { - if (newSplit.projectOwner) { - console.error( - 'Not supporting manually adding project owner splits in Edit cycle form', - ) - return - } - handleNewPayoutSplit({ newSplit }) - setAddRecipientModalOpen(false) - } - - return ( - <> - -
-
- Payout recipients -
- {hideExplaination ? null : ( -
- - Juicebox provides trustless payroll capabilities to run - automated payouts completely on-chain.{' '} - - Learn more about payouts - - -
- )} -
-
-
- {addPayoutsDisabled ? null : ( - - )} - {hideSettings ? null : } -
-
-
- setAddRecipientModalOpen(false)} - hideProjectOwnerOption - hideFee - /> - - ) -} diff --git a/src/packages/v4/components/PayoutsTable/PayoutSplitRow.tsx b/src/packages/v4/components/PayoutsTable/PayoutSplitRow.tsx deleted file mode 100644 index 7deaac5aaa..0000000000 --- a/src/packages/v4/components/PayoutsTable/PayoutSplitRow.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import FormattedNumberInput from 'components/inputs/FormattedNumberInput' -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' -import { PayoutsTableRow } from 'components/PayoutsTable/PayoutsTableRow' -import { JBSplit as Split } from 'juice-sdk-core' -import round from 'lodash/round' -import { useState } from 'react' -import { AddEditAllocationModal, AddEditAllocationModalEntity } from '../Allocation/AddEditAllocationModal' -import { usePayoutsTableContext } from './context/PayoutsTableContext' -import { usePayoutsTable } from './hooks/usePayoutsTable' -import { PayoutSplitRowMenu } from './PayoutSplitRowMenu' -import { PayoutTitle } from './PayoutTitle' - -const Cell = PayoutsTableCell - -export function PayoutSplitRow({ - payoutSplit, - onDeleteClick, -}: { - payoutSplit: Split - onDeleteClick: VoidFunction -}) { - const [editModalOpen, setEditModalOpen] = useState(false) - const [ - amountPercentFieldHasEndingDecimal, - setAmountPercentFieldHasEndingDecimal, - ] = useState(false) - - const { setPayoutSplits } = usePayoutsTableContext() - const canEditSplits = Boolean(setPayoutSplits) - - const { - currencyOrPercentSymbol, - derivePayoutAmount, - formattedPayoutPercent, - roundingPrecision, - handlePayoutSplitChanged, - handlePayoutSplitAmountChanged, - distributionLimitIsInfinite, - } = usePayoutsTable() - const amount = derivePayoutAmount({ payoutSplit }) - const isPercent = distributionLimitIsInfinite - - let formattedAmountOrPercentage = isPercent - ? formattedPayoutPercent({ payoutSplitPercent: Number(payoutSplit.percent.value) }) - : round(amount, roundingPrecision).toString() - - if (!canEditSplits) { - formattedAmountOrPercentage = `${currencyOrPercentSymbol}${formattedAmountOrPercentage}` - } - - const onAmountPercentageInputChange = (val: string | undefined) => { - setAmountPercentFieldHasEndingDecimal(Boolean(val?.endsWith('.'))) - - const newAmount = parseFloat(val ?? '0') - if (isPercent) { - handlePayoutSplitChanged({ - editedPayoutSplit: payoutSplit, - newPayoutSplit: { - ...payoutSplit, - projectId: payoutSplit.projectId.toString(), - projectOwner: false, - amount: { - value: newAmount.toString(), - isPercent, - }, - }, - }) - } else { - handlePayoutSplitAmountChanged({ - editingPayoutSplit: payoutSplit, - newAmount, - }) - } - } - - const handleEditModalOk = (allocation: AddEditAllocationModalEntity) => { - if (allocation.projectOwner) { - console.error( - 'Not supporting manually adding project owner splits in Edit cycle form', - ) - return - } - handlePayoutSplitChanged({ - editedPayoutSplit: payoutSplit, - newPayoutSplit: allocation, - }) - setEditModalOpen(false) - } - - const addEditAllocationModalEntity = { - projectOwner: false, - beneficiary: payoutSplit.beneficiary, - projectId: payoutSplit.projectId.toString(), - amount: { - value: formattedAmountOrPercentage, - isPercent, - }, - lockedUntil: payoutSplit.lockedUntil, - } as AddEditAllocationModalEntity - - const _value = `${formattedAmountOrPercentage}${ - amountPercentFieldHasEndingDecimal ? '.' : '' - }` - - const paddingY = canEditSplits ? 'py-6' : 'py-3' - - return ( - <> - - - - - - {setPayoutSplits ? ( -
- {currencyOrPercentSymbol} - } - accessoryPosition="left" - value={_value} - onChange={onAmountPercentageInputChange} - className="h-10 w-28 md:w-full" - /> - setEditModalOpen(true)} - onDeleteClick={onDeleteClick} - /> -
- ) : ( - _value - )} -
-
- setEditModalOpen(false)} - hideProjectOwnerOption - hideFee - /> - - ) -} diff --git a/src/packages/v4/components/PayoutsTable/PayoutSplitRowMenu.tsx b/src/packages/v4/components/PayoutsTable/PayoutSplitRowMenu.tsx deleted file mode 100644 index fa5e26610c..0000000000 --- a/src/packages/v4/components/PayoutsTable/PayoutSplitRowMenu.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline' -import { Trans } from '@lingui/macro' -import { PopupMenu } from 'components/ui/PopupMenu' - -export function PayoutSplitRowMenu({ - onEditClick, - onDeleteClick, -}: { - onEditClick: VoidFunction - onDeleteClick: VoidFunction -}) { - const menuItemsLabelClass = 'flex gap-2 items-center' - const menuItemsIconClass = 'h-5 w-5' - - const menuItems = [ - { - id: 'edit', - label: ( -
- - Edit -
- ), - onClick: onEditClick, - }, - { - id: 'delete', - label: ( -
- - Delete -
- ), - onClick: onDeleteClick, - }, - ] - - return -} diff --git a/src/packages/v4/components/PayoutsTable/PayoutTableSettings.tsx b/src/packages/v4/components/PayoutsTable/PayoutTableSettings.tsx deleted file mode 100644 index 6789390223..0000000000 --- a/src/packages/v4/components/PayoutsTable/PayoutTableSettings.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { ReceiptPercentIcon, TrashIcon } from '@heroicons/react/24/outline' -import { Trans } from '@lingui/macro' -import { SwitchToUnlimitedModal } from 'components/PayoutsTable/SwitchToUnlimitedModal' -import { PopupMenu, PopupMenuItem } from 'components/ui/PopupMenu' -import { handleConfirmationDeletion } from 'hooks/emitConfirmationDeletionModal' -import { useState } from 'react' -import { ReduxDistributionLimit } from 'redux/hooks/useEditingDistributionLimit' -import { fromWad } from 'utils/format/formatNumber' -import { ConvertAmountsModal } from './ConvertAmountsModal' -import { usePayoutsTable } from './hooks/usePayoutsTable' - -export const payoutsTableMenuItemsLabelClass = 'flex gap-2 items-center text-sm' -export const payoutsTableMenuItemsIconClass = 'h-5 w-5' - -export function PayoutTableSettings() { - const [switchToUnlimitedModalOpen, setSwitchToUnlimitedModalOpen] = - useState(false) - const [switchToLimitedModalOpen, setSwitchToLimitedModalOpen] = - useState(false) - - const { - payoutSplits, - distributionLimitIsInfinite, - handleDeleteAllPayoutSplits, - setDistributionLimit, - setCurrency, - setSplits100Percent, - } = usePayoutsTable() - - const handleSwitchToLimitedPayouts = (newLimit: ReduxDistributionLimit) => { - setDistributionLimit(parseFloat(fromWad(newLimit.amount))) - setCurrency(newLimit.currency) - setSwitchToLimitedModalOpen(false) - } - - const handleSwitchToUnlimitedPayouts = () => { - setDistributionLimit(undefined) - setSplits100Percent() - setSwitchToUnlimitedModalOpen(false) - } - - let menuItems: PopupMenuItem[] = [] - - if (distributionLimitIsInfinite) { - menuItems = [ - ...menuItems, - { - id: 'limited', - label: ( -
- - Switch to limited -
- ), - onClick: () => setSwitchToLimitedModalOpen(true), - }, - ] - } else { - menuItems = [ - ...menuItems, - { - id: 'unlimited', - label: ( -
- - Switch to unlimited -
- ), - onClick: () => setSwitchToUnlimitedModalOpen(true), - }, - ] - } - - if (payoutSplits.length > 0) { - menuItems = [ - ...menuItems, - { - id: 'delete', - label: ( -
- - Delete all -
- ), - onClick: handleConfirmationDeletion({ - type: 'all payout recipients', - onConfirm: handleDeleteAllPayoutSplits, - }), - }, - ] - } - - return ( - <> - - setSwitchToUnlimitedModalOpen(false)} - onOk={handleSwitchToUnlimitedPayouts} - /> - setSwitchToLimitedModalOpen(false)} - splits={payoutSplits} - /> - - ) -} diff --git a/src/packages/v4/components/PayoutsTable/PayoutTitle.tsx b/src/packages/v4/components/PayoutsTable/PayoutTitle.tsx deleted file mode 100644 index 1c27df60b2..0000000000 --- a/src/packages/v4/components/PayoutsTable/PayoutTitle.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { LockFilled } from '@ant-design/icons' -import { t } from '@lingui/macro' -import { Tooltip } from 'antd' -import EthereumAddress from 'components/EthereumAddress' -import { JBSplit as Split } from 'juice-sdk-core' -import { useRouter } from 'next/router' -import { formatDate } from 'utils/format/formatDate' -import V4ProjectHandleLink from '../V4ProjectHandleLink' -import { usePayoutsTableContext } from './context/PayoutsTableContext' - -export function PayoutTitle({ payoutSplit }: { payoutSplit: Split }) { - const router = useRouter() - const { chainName } = router.query - - const { showAvatars } = usePayoutsTableContext() - - const isProject = - Boolean(payoutSplit.projectId) && payoutSplit.projectId !== 0n - - return ( -
- {isProject ? ( - - ) : ( - - )} - {!!payoutSplit.lockedUntil && ( - - - - )} -
- ) -} diff --git a/src/packages/v4/components/PayoutsTable/PayoutsTable.tsx b/src/packages/v4/components/PayoutsTable/PayoutsTable.tsx deleted file mode 100644 index e6a30722d9..0000000000 --- a/src/packages/v4/components/PayoutsTable/PayoutsTable.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { PayoutsTableBody } from './PayoutsTableBody' -import { - PayoutsTableContext, - PayoutsTableContextProps, -} from './context/PayoutsTableContext' - -export function PayoutsTable(props: PayoutsTableContextProps) { - return ( - - - - ) -} diff --git a/src/packages/v4/components/PayoutsTable/PayoutsTableBody.tsx b/src/packages/v4/components/PayoutsTable/PayoutsTableBody.tsx deleted file mode 100644 index 8a5e02ef85..0000000000 --- a/src/packages/v4/components/PayoutsTable/PayoutsTableBody.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Form } from 'antd' -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' -import { PayoutsTableRow } from 'components/PayoutsTable/PayoutsTableRow' -import { getV4CurrencyOption } from 'packages/v4/utils/currency' -import { twMerge } from 'tailwind-merge' -import { Allocation } from '../Allocation/Allocation' -import { CurrencySwitcher } from './CurrencySwitcher' -import { HeaderRows } from './HeaderRows' -import { PayoutSplitRow } from './PayoutSplitRow' -import { TotalRows } from './TotalRows' -import { usePayoutsTableContext } from './context/PayoutsTableContext' -import { usePayoutsTable } from './hooks/usePayoutsTable' - -const Row = PayoutsTableRow -const Cell = PayoutsTableCell - -export function PayoutsTableBody() { - const { topAccessory, hideHeader } = usePayoutsTableContext() - const { - payoutSplits, - currency, - handleDeletePayoutSplit, - setCurrency, - distributionLimit, - } = usePayoutsTable() - const emptyState = distributionLimit === 0 && !payoutSplits?.length - - const hasDistributionLimit = distributionLimit && distributionLimit > 0 - - return ( - <> - {topAccessory} -
- -
- {hideHeader ? null : } -
- {emptyState ? ( - - - No payout recipients - - - ) : ( - <> - {/* `|| hasDistributionLimit` to account for old projects whose payout is only the "remaining project owner" split, but still have a distributionLimit. */} - {payoutSplits.length > 0 || hasDistributionLimit ? ( - - - Address or ID - - - - - - ) : null} - {payoutSplits.map((payoutSplit, index) => ( - - handleDeletePayoutSplit({ payoutSplit }) - } - /> - ))} - - - )} -
-
-
-
- {/* Empty form items just to keep AntD useWatch happy */} - - - - - ) -} diff --git a/src/packages/v4/components/PayoutsTable/TotalRows.tsx b/src/packages/v4/components/PayoutsTable/TotalRows.tsx deleted file mode 100644 index b055dce230..0000000000 --- a/src/packages/v4/components/PayoutsTable/TotalRows.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { Trans, t } from '@lingui/macro' -import { Tooltip } from 'antd' -import EthereumAddress from 'components/EthereumAddress' -import { PayoutsTableCell } from 'components/PayoutsTable/PayoutsTableCell' -import { PayoutsTableRow } from 'components/PayoutsTable/PayoutsTableRow' -import TooltipLabel from 'components/TooltipLabel' -import round from 'lodash/round' -import useProjectOwnerOf from 'packages/v4/hooks/useV4ProjectOwnerOf' -import { usePayoutsTable } from './hooks/usePayoutsTable' - -const Row = PayoutsTableRow -const Cell = PayoutsTableCell - -const SMALL_FEE_PRECISION_BUFFER = 2 - -/* Bottom few rows of the payouts table which show total amounts and fees */ -export function TotalRows() { - const { - distributionLimit, - distributionLimitIsInfinite, - totalFeeAmount, - subTotal, - roundingPrecision, - payoutSplits, - ownerRemainderValue, - currencyOrPercentSymbol, - } = usePayoutsTable() - - const formattedDistributionLimit = - distributionLimit !== undefined && !distributionLimitIsInfinite - ? round(distributionLimit, roundingPrecision) - : t`Unlimited` - - const { data: projectOwnerAddress } = useProjectOwnerOf() - - const subTotalExceedsMax = distributionLimitIsInfinite && subTotal > 100 - - // Make fee more precise when it is very small - const feeRoundingPrecision = - totalFeeAmount >= 1 - ? roundingPrecision - : roundingPrecision + SMALL_FEE_PRECISION_BUFFER - - const wholePayoutToRemainingOwner = - distributionLimit && distributionLimit > 0 && payoutSplits.length === 0 - const remainingOwnerLabel = wholePayoutToRemainingOwner ? ( - - ) : ( - Remaining (to project owner)} - /> - ) - return ( - <> - {wholePayoutToRemainingOwner ? null : ( - - Sub-total - - Sub-total cannot exceed 100% - ) : undefined - } - > - {currencyOrPercentSymbol} {round(subTotal, roundingPrecision)} - - - - )} - {ownerRemainderValue > 0 ? ( - - {remainingOwnerLabel} - - {currencyOrPercentSymbol} {ownerRemainderValue} - - - ) : null} - - Fees - - {currencyOrPercentSymbol}{' '} - {round(totalFeeAmount, feeRoundingPrecision)} - - - - Total - - <> - {distributionLimitIsInfinite ? null : ( - <>{currencyOrPercentSymbol} - )} - {formattedDistributionLimit} - - - - - ) -} diff --git a/src/packages/v4/components/PayoutsTable/context/PayoutsTableContext.tsx b/src/packages/v4/components/PayoutsTable/context/PayoutsTableContext.tsx deleted file mode 100644 index 401730b83f..0000000000 --- a/src/packages/v4/components/PayoutsTable/context/PayoutsTableContext.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { CurrencyName } from 'constants/currency' -import { JBSplit as Split } from 'juice-sdk-core' -import { ReactNode, createContext, useContext } from 'react' - -export interface PayoutsTableContextProps { - payoutSplits: Split[] - setPayoutSplits?: (payoutSplits: Split[]) => void - currency: CurrencyName - setCurrency?: (currency: CurrencyName) => void - distributionLimit: number | undefined - setDistributionLimit?: (distributionLimit: number | undefined) => void - hideExplaination?: boolean - hideHeader?: boolean - showAvatars?: boolean - topAccessory?: ReactNode - hideSettings?: boolean - addPayoutsDisabled?: boolean -} - -export const PayoutsTableContext = createContext< - PayoutsTableContextProps | undefined ->(undefined) - -export const usePayoutsTableContext = () => { - const context = useContext(PayoutsTableContext) - if (!context) { - throw new Error( - 'usePayoutsTableContext must be used within a PayoutsTableProvider', - ) - } - return context -} diff --git a/src/packages/v4/components/PayoutsTable/hooks/usePayoutsTable.tsx b/src/packages/v4/components/PayoutsTable/hooks/usePayoutsTable.tsx deleted file mode 100644 index 6b3971cce7..0000000000 --- a/src/packages/v4/components/PayoutsTable/hooks/usePayoutsTable.tsx +++ /dev/null @@ -1,412 +0,0 @@ -import * as constants from '@ethersproject/constants' -import { NULL_ALLOCATOR_ADDRESS } from 'constants/contracts/mainnet/Allocators' -import { ONE_BILLION, WAD_DECIMALS } from 'constants/numbers' -import { SPLITS_TOTAL_PERCENT, JBSplit as Split, SplitPortion } from 'juice-sdk-core' -import isEqual from 'lodash/isEqual' -import round from 'lodash/round' -import { AddEditAllocationModalEntity } from 'packages/v4/components/Allocation/AddEditAllocationModal' -import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption' -import { V4CurrencyName, V4_CURRENCY_METADATA, getV4CurrencyOption } from 'packages/v4/utils/currency' -import { - JB_FEE, - adjustedSplitPercents, - deriveAmountAfterFee, - deriveAmountBeforeFee, - derivePayoutAmount, - ensureSplitsSumTo100Percent, - getNewDistributionLimit, -} from 'packages/v4/utils/distributions' -import { - MAX_PAYOUT_LIMIT, -} from 'packages/v4/utils/math' -import { - hasEqualRecipient, - isJuiceboxProjectSplit, - isProjectSplit, - totalSplitsPercent, v4GetProjectOwnerRemainderSplit, -} from 'packages/v4/utils/v4Splits' -import { useMemo } from 'react' -import { parseWad } from 'utils/format/formatNumber' -import { usePayoutsTableContext } from '../context/PayoutsTableContext' - -export const usePayoutsTable = () => { - const { - payoutSplits, - setPayoutSplits, - distributionLimit, - setDistributionLimit, - currency, - setCurrency: setCurrencyName, - } = usePayoutsTableContext() - // const { setFormHasUpdated } = useEditCycleFormContext() // TODO: Settings - const distributionLimitIsInfinite = useMemo( - () => - distributionLimit === undefined || - parseWad(distributionLimit).eq(MAX_PAYOUT_LIMIT), - [distributionLimit], - ) - - const amountOrPercentValue = ({ - payoutSplit, - dontApplyFee, - }: { - payoutSplit: Split - dontApplyFee?: boolean - }) => - distributionLimitIsInfinite - ? (payoutSplit.percent.toFloat() / ONE_BILLION) * 100 - : _derivePayoutAmount({ payoutSplit, dontApplyFee }) - - /* Total amount that leaves the treasury minus fees */ - const subTotal = payoutSplits.reduce((acc, payoutSplit) => { - const reducer = amountOrPercentValue({ payoutSplit }) - return acc + reducer - }, 0) - - let roundingPrecision = currency === 'ETH' ? 4 : 2 - // If subTotal exceeds 100%, set rounding precision to exceeding decimal amount - // e.g. subTotal = 100.00001, roundingPrecision = 5 - if (distributionLimitIsInfinite && subTotal > 100) { - const decimalPart = subTotal - Math.floor(subTotal) - if (decimalPart > 0) { - const decimalStr = decimalPart.toString() - const decimalPrecision = decimalStr.slice( - decimalStr.indexOf('.') + 1, - ).length - roundingPrecision = Math.max(roundingPrecision, decimalPrecision) - } - } - - const ownerRemainingPercentPPB = - SPLITS_TOTAL_PERCENT - totalSplitsPercent(payoutSplits) // parts-per-billion - const ownerRemainingAmount = - distributionLimit && !distributionLimitIsInfinite - ? deriveAmountAfterFee( - (ownerRemainingPercentPPB / ONE_BILLION) * distributionLimit, - ) - : undefined - - const ownerRemainderValue = round( - ownerRemainingAmount ?? (ownerRemainingPercentPPB / ONE_BILLION) * 100, - roundingPrecision, - ) - - const currencyOrPercentSymbol = distributionLimitIsInfinite - ? '%' - : V4_CURRENCY_METADATA[getV4CurrencyOption(currency)].symbol - - /* Payouts that don't go to Juicebox projects incur 2.5% fee */ - const nonJuiceboxProjectPayoutSplits = [ - ...payoutSplits.filter(payoutSplit => !isJuiceboxProjectSplit(payoutSplit)), - v4GetProjectOwnerRemainderSplit( - // remaining owner split also incurs fee - constants.AddressZero, - payoutSplits, - ) as Split, - ] - - /* Count the total fee amount. If % of payouts sums > 100, just set fees to 2.5% (maximum)*/ - const totalFeeAmount = - distributionLimitIsInfinite && round(subTotal, roundingPrecision) > 100 - ? 2.5 - : nonJuiceboxProjectPayoutSplits.reduce((acc, payoutSplit) => { - return ( - acc + - amountOrPercentValue({ payoutSplit, dontApplyFee: true }) * JB_FEE - ) - }, 0) - - /** - * Sets the currency for the distributionLimit - * @param currency - Currency as a V4CurrencyOption (1 | 2) - */ - function setCurrency(currency: V4CurrencyOption) { - setCurrencyName?.(V4CurrencyName(currency) ?? 'ETH') - // setFormHasUpdated(true) TODO: Settings - } - - function setSplits100Percent() { - setPayoutSplits?.(ensureSplitsSumTo100Percent({ splits: payoutSplits })) - } - - function _setPayoutSplits(splits: Split[]) { - if (distributionLimitIsInfinite) { - setPayoutSplits?.(splits) - } else { - setPayoutSplits?.(ensureSplitsSumTo100Percent({ splits })) - } - // setFormHasUpdated(true) - } - - function _setDistributionLimit(distributionLimit: number | undefined) { - const _distributionLimit = - distributionLimit !== undefined - ? round(distributionLimit, WAD_DECIMALS) - : undefined - setDistributionLimit?.(_distributionLimit) - } - - /** - * Derive payout amount from the % of the distributionLimit - * @param percent - Percent of distributionLimit in parts-per-billion (PPB) - * @returns Amount in the distributionLimitCurrency. - */ - function _derivePayoutAmount({ - payoutSplit, - dontApplyFee, - }: { - payoutSplit: Split - dontApplyFee?: boolean - }) { - return derivePayoutAmount({ - payoutSplit, - distributionLimit, - dontApplyFee, - }) - } - - /** - * Convert parts-per-billion percent to formatted percent - * @param percent - Percent of distributionLimit in parts-per-billion (PPB) - * @returns Percent in standard format - */ - function formattedPayoutPercent({ - payoutSplitPercent, - }: { - payoutSplitPercent: number - }) { - return round( - (payoutSplitPercent / ONE_BILLION) * 100, - roundingPrecision, - ).toString() - } - - /** - * Handle payoutSplit added: - * - Sets new distributionLimit (DL) based on sum of new payout amounts - * - Changed the % of other splits based on the new DL keep their amount the same - * @param newSplit - Just added split - * @param newAmount - The new amount of the @editingPayoutSplit - */ - function handleNewPayoutSplit({ - newSplit, - }: { - newSplit: AddEditAllocationModalEntity & { projectOwner: false } - }) { - const newSplitPercent = parseFloat(newSplit.amount.value) - let newSplitPercentPPB = (newSplitPercent * ONE_BILLION) / 100 - let adjustedSplits: Split[] = payoutSplits - let newDistributionLimit = distributionLimit - - const isProject = newSplit.projectId && newSplit.projectId !== '0x00' - - // If amounts (!distributionLimitIsInfinite), handle changing DL and split %s - if (!distributionLimitIsInfinite) { - const newAmount = isProject - ? newSplitPercent - : deriveAmountBeforeFee(newSplitPercent) - // Convert the newAmount to its percentage of the new DL in parts-per-bill - newDistributionLimit = distributionLimit - ? getNewDistributionLimit({ - currentDistributionLimit: distributionLimit.toString(), - newSplitAmount: newAmount, - editingSplitPercent: 0, - ownerRemainingAmount, - }) - : newAmount - - newSplitPercentPPB = round( - (newAmount / (newDistributionLimit ?? 0)) * ONE_BILLION, - ) - - // recalculate all split percents based on newly added split amount - if (newDistributionLimit && !distributionLimitIsInfinite) { - adjustedSplits = adjustedSplitPercents({ - splits: payoutSplits, - oldDistributionLimit: (distributionLimit as number).toString() ?? '0', - newDistributionLimit: newDistributionLimit.toString(), - }) - } - _setDistributionLimit(newDistributionLimit) - } - - const newPayoutSplit = { - beneficiary: newSplit.beneficiary, - percent: new SplitPortion(newSplitPercentPPB), - preferAddToBalance: false, - lockedUntil: newSplit.lockedUntil ?? 0, - projectId: newSplit.projectId ?? 0n, - hook: NULL_ALLOCATOR_ADDRESS, - } as Split - - const newPayoutSplits = [...adjustedSplits, newPayoutSplit] - - _setPayoutSplits(newPayoutSplits) - } - - /** - * Handle payoutSplit changed through the Edit modal: - * - Changes relevant split properties - * - If amount changed, calls handlePayoutSplitAmountChanged - * @param editedPayoutSplit - Split that has been edited - * @param newPayoutSplit - The new payout split to replace @editedPayoutSplit - */ - function handlePayoutSplitChanged({ - editedPayoutSplit, - newPayoutSplit, - }: { - editedPayoutSplit: Split - newPayoutSplit: AddEditAllocationModalEntity & { projectOwner: false } - }) { - let newSplit: Split = editedPayoutSplit - // Find editedPayoutSplit in payoutSplits and change it to newPayoutSplit - const newSplits = payoutSplits.map(m => { - if (hasEqualRecipient(m, editedPayoutSplit)) { - newSplit = { - ...newSplit, - beneficiary: newPayoutSplit.beneficiary ?? constants.AddressZero, - lockedUntil: newPayoutSplit.lockedUntil ?? 0, - projectId: BigInt(newPayoutSplit.projectId ?? '0'), - } - // If percents (distributionLimitIsInfinite), further alterations to percentages are not needed. - // In this case, set the percent now. - if (distributionLimitIsInfinite && !newPayoutSplit.projectOwner) { - newSplit = { - ...newSplit, - percent: - new SplitPortion((parseFloat(newPayoutSplit.amount.value) * ONE_BILLION) / 100), - } - } - return newSplit - } - return m - }) - _setPayoutSplits(newSplits) - if (!distributionLimitIsInfinite && !newPayoutSplit.projectOwner) { - handlePayoutSplitAmountChanged({ - editingPayoutSplit: newSplit, - newAmount: parseFloat(newPayoutSplit.amount.value), - newSplits, - }) - } - } - - /** - * Handle payoutSplit amount changed (called when payouts table rows' input fields update): - * - Sets new distributionLimit (DL) based on sum of new payout amounts - * - Changed the % of other splits based on the new DL keep their amount the same - * @param editingPayoutSplit - Split that has had its amount changed - * @param newAmount - The new amount of the @editingPayoutSplit - * @param newSplits - (optional) pass new splits to adjust. If undefined, uses current @payoutSplits state - */ - function handlePayoutSplitAmountChanged({ - editingPayoutSplit, - newAmount, - newSplits, - }: { - editingPayoutSplit: Split - newAmount: number - newSplits?: Split[] - }) { - const isNaN = Number.isNaN(newAmount) - const _amount = isNaN - ? 0 - : isProjectSplit(editingPayoutSplit) - ? newAmount - : deriveAmountBeforeFee(newAmount) - // Convert the newAmount to its percentage of the new DL in parts-per-bill - const newDistributionLimit = - distributionLimit !== undefined - ? getNewDistributionLimit({ - currentDistributionLimit: distributionLimit.toString(), - newSplitAmount: _amount, - editingSplitPercent: editingPayoutSplit.percent.toFloat(), - ownerRemainingAmount, - }) - : undefined // undefined means DL is infinite - const newSplitPercentPPB = round( - (_amount / (newDistributionLimit ?? 0)) * ONE_BILLION, - ) - let adjustedSplits: Split[] = newSplits ?? payoutSplits - // recalculate all split percents based on newly added split amount - if (newDistributionLimit && !distributionLimitIsInfinite) { - adjustedSplits = adjustedSplitPercents({ - splits: adjustedSplits, - oldDistributionLimit: (distributionLimit as number).toString() ?? '0', - newDistributionLimit: newDistributionLimit.toString(), - }) - } - - const newPayoutSplit = { - ...editingPayoutSplit, - percent: new SplitPortion(newSplitPercentPPB), - } as Split - - const newPayoutSplits = adjustedSplits.map(m => { - return hasEqualRecipient(m, editingPayoutSplit) - ? { - ...m, - ...newPayoutSplit, - } - : m - }) - _setDistributionLimit(newDistributionLimit) - _setPayoutSplits(newPayoutSplits) - } - - /** - * Handle payoutSplit amount deleted: - * - Deletes a payout split and adjusts the distributionLimit (DL) accordingly - * - Changed the % of other splits based on the new DL keep their amount the same - * @param split - Split to be deleted - */ - function handleDeletePayoutSplit({ payoutSplit }: { payoutSplit: Split }) { - const newSplits = payoutSplits.filter(m => !isEqual(m, payoutSplit)) - - let adjustedSplits: Split[] = newSplits - let newDistributionLimit = distributionLimit - if (distributionLimit && !distributionLimitIsInfinite) { - const currentAmount = _derivePayoutAmount({ - payoutSplit, - dontApplyFee: true, - }) - newDistributionLimit = distributionLimit - currentAmount - adjustedSplits = adjustedSplitPercents({ - splits: newSplits, - oldDistributionLimit: (distributionLimit as number).toString() ?? '0', - newDistributionLimit: newDistributionLimit.toString(), - }) - } - - _setDistributionLimit(newDistributionLimit) - _setPayoutSplits(adjustedSplits) - } - - function handleDeleteAllPayoutSplits() { - setDistributionLimit?.(0) - _setPayoutSplits([]) - } - - return { - distributionLimit, - distributionLimitIsInfinite, - setDistributionLimit: _setDistributionLimit, - currency, - setCurrency, - currencyOrPercentSymbol, - payoutSplits, - setPayoutSplits: _setPayoutSplits, - setSplits100Percent, - derivePayoutAmount: _derivePayoutAmount, - formattedPayoutPercent, - roundingPrecision, - handlePayoutSplitAmountChanged, - handleNewPayoutSplit, - handlePayoutSplitChanged, - handleDeletePayoutSplit, - handleDeleteAllPayoutSplits, - subTotal, - ownerRemainderValue, - totalFeeAmount, - } -} diff --git a/src/packages/v4/components/SplitList/SplitItem/EthAddressBeneficiary.tsx b/src/packages/v4/components/SplitList/SplitItem/EthAddressBeneficiary.tsx deleted file mode 100644 index fe06f096be..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/EthAddressBeneficiary.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { CrownFilled } from '@ant-design/icons' -import { Trans } from '@lingui/macro' -import { Tooltip } from 'antd' -import { JuiceboxAccountLink } from 'components/JuiceboxAccountLink' -import { isEqualAddress } from 'utils/address' - -export function ETHAddressBeneficiary({ - beneficaryAddress, - projectOwnerAddress, - hideAvatar, -}: { - beneficaryAddress: string | undefined - projectOwnerAddress: string | undefined - hideAvatar?: boolean -}) { - const isProjectOwner = isEqualAddress(projectOwnerAddress, beneficaryAddress) - - return ( -
- {beneficaryAddress ? ( - - ) : null} - {!beneficaryAddress && isProjectOwner ? ( - Project owner (you) - ) : null} - {isProjectOwner && ( - Project owner}> - - - )} - : -
- ) -} diff --git a/src/packages/v4/components/SplitList/SplitItem/JuiceboxProjectBeneficiary.tsx b/src/packages/v4/components/SplitList/SplitItem/JuiceboxProjectBeneficiary.tsx deleted file mode 100644 index 48cd9daa22..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/JuiceboxProjectBeneficiary.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Tooltip } from 'antd' -import { AllocatorBadge } from 'components/AllocatorBadge' -import { NULL_ALLOCATOR_ADDRESS } from 'constants/contracts/mainnet/Allocators' -import { JBSplit } from 'juice-sdk-core' -import { useRouter } from 'next/router' -import V4ProjectHandleLink from '../../V4ProjectHandleLink' -export function JuiceboxProjectBeneficiary({ - split, - value, -}: { - split: JBSplit - value?: string | JSX.Element -}) { - const router = useRouter() - const { chainName } = router.query - - if (!split.projectId) return null - - return ( -
-
- - -
- {split.hook === NULL_ALLOCATOR_ADDRESS ? ( -
- {value ? ( - <> - - This address receives the tokens minted by this payout. - - } - > - - Tokens: - - - {value} - - ) : null} -
- ) : null} -
- ) -} diff --git a/src/packages/v4/components/SplitList/SplitItem/LockedUntilValue.tsx b/src/packages/v4/components/SplitList/SplitItem/LockedUntilValue.tsx deleted file mode 100644 index 749bbf8471..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/LockedUntilValue.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { LockOutlined } from '@ant-design/icons' -import { Trans } from '@lingui/macro' -import { Tooltip } from 'antd' -import { formatDate } from 'utils/format/formatDate' - -export function LockedUntilValue({ - lockedUntil, - value, -}: { - lockedUntil?: number | undefined - value?: JSX.Element | string -}) { - const hasLockedUntil = lockedUntil && lockedUntil > 0n - - const lockedUntilFormatted = hasLockedUntil - ? formatDate(lockedUntil * 1000, 'yyyy-MM-DD') - : undefined - - if (!lockedUntilFormatted) return null - - return ( - - Locked until {value} - - } - className="h-22px ml-2 flex items-center text-sm text-grey-500 dark:text-grey-300" - > - -
{value ?? lockedUntilFormatted}
-
- ) -} diff --git a/src/packages/v4/components/SplitList/SplitItem/ReservedTokensValue.tsx b/src/packages/v4/components/SplitList/SplitItem/ReservedTokensValue.tsx deleted file mode 100644 index efa8838671..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/ReservedTokensValue.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Trans } from '@lingui/macro' -import TooltipIcon from 'components/TooltipIcon' -import { SplitPortion } from 'juice-sdk-core' - -export function ReservedTokensValue({ - splitPercent, - reservedPercent, -}: { - splitPercent: SplitPortion - reservedPercent: number -}) { - const splitPercentNum = splitPercent.toFloat() - return ( - - {(reservedPercent * splitPercentNum) / 100}% of total token issuance. - - } - /> - ) -} diff --git a/src/packages/v4/components/SplitList/SplitItem/SplitAmountValue.tsx b/src/packages/v4/components/SplitList/SplitItem/SplitAmountValue.tsx deleted file mode 100644 index a83f6e951a..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/SplitAmountValue.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { DollarCircleOutlined } from '@ant-design/icons' -import { Trans } from '@lingui/macro' -import { Tooltip } from 'antd' -import { CurrencyName } from 'constants/currency' -import { SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' -import { NativeTokenValue, useReadJbMultiTerminalFee } from 'juice-sdk-react' -import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption' -import { V4CurrencyName } from 'packages/v4/utils/currency' -import { isJuiceboxProjectSplit } from 'packages/v4/utils/v4Splits' -import { formatWad } from 'utils/format/formatNumber' -import { feeForAmount } from 'utils/math' -import { SplitProps } from './SplitItem' - -export function SplitAmountValue({ - props, - hideTooltip, -}: { - props: SplitProps - hideTooltip?: boolean -}) { - const { data: primaryNativeTerminalFee } = useReadJbMultiTerminalFee() - - const splitValue = props.totalValue - ? (props.totalValue * props.split.percent.value) / - BigInt(SPLITS_TOTAL_PERCENT) - : undefined - - const isJuiceboxProject = isJuiceboxProjectSplit(props.split) - const hasFee = !isJuiceboxProject && !props.dontApplyFeeToAmount - const feeAmount = hasFee - ? feeForAmount(splitValue, primaryNativeTerminalFee) ?? 0n - : 0n - const valueAfterFees = splitValue ? splitValue - feeAmount : 0 - - const currencyName = V4CurrencyName( - Number(props.currency) as V4CurrencyOption, - ) - - const createTooltipTitle = ( - curr: CurrencyName | undefined, - amount: bigint | undefined, - ) => { - if (hideTooltip || !amount) return undefined - return - } - - return ( - <> - - {valueAfterFees ? ( - <> - {currencyName ? ( - // - - ) : ( - // if no currency, assume its a token with 18 decimals (a wad) - <>{formatWad(valueAfterFees, { precision: 2 })} - )} - {props.valueSuffix ? {props.valueSuffix} : null} - - ) : null} - - - {props.showFee && !isJuiceboxProject && ( - - - fee - - } - className="ml-1" - > - - - )} - - ) -} diff --git a/src/packages/v4/components/SplitList/SplitItem/SplitItem.tsx b/src/packages/v4/components/SplitList/SplitItem/SplitItem.tsx deleted file mode 100644 index 4b5274128d..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/SplitItem.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { JBSplit } from 'juice-sdk-core' -import { isJuiceboxProjectSplit } from 'packages/v4/utils/v4Splits' -import { ETHAddressBeneficiary } from './EthAddressBeneficiary' -import { JuiceboxProjectBeneficiary } from './JuiceboxProjectBeneficiary' -import { LockedUntilValue } from './LockedUntilValue' -import { ReservedTokensValue } from './ReservedTokensValue' -import { SplitValue } from './SplitValue' - -export type SplitProps = { - split: JBSplit - totalValue: bigint | undefined - projectOwnerAddress: string | undefined - reservedPercent?: number - valueSuffix?: string | JSX.Element - valueFormatProps?: { precision?: number } - currency?: bigint - oldCurrency?: bigint - showAmount?: boolean - showFee?: boolean - dontApplyFeeToAmount?: boolean -} - -export function SplitItem({ props }: { props: SplitProps }) { - const isJuiceboxProject = isJuiceboxProjectSplit(props.split) - - return ( -
-
-
- {isJuiceboxProject ? ( - - ) : ( - - )} -
- - {props.split.lockedUntil && props.split.lockedUntil > 0 ? ( - - ) : null} -
-
- - {props.reservedPercent !== undefined ? ( - - ) : null} -
-
- ) -} diff --git a/src/packages/v4/components/SplitList/SplitItem/SplitPercentValue.tsx b/src/packages/v4/components/SplitList/SplitItem/SplitPercentValue.tsx deleted file mode 100644 index 8d8d603479..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/SplitPercentValue.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { SplitPortion } from 'juice-sdk-core' - -export function SplitPercentValue({ percent }: { percent: SplitPortion }) { - const formattedPercent = percent.formatPercentage() - - return {formattedPercent}% -} diff --git a/src/packages/v4/components/SplitList/SplitItem/SplitValue.tsx b/src/packages/v4/components/SplitList/SplitItem/SplitValue.tsx deleted file mode 100644 index 81dde36822..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/SplitValue.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Parenthesis } from 'components/Parenthesis' -import { SplitAmountValue } from './SplitAmountValue' -import { SplitProps } from './SplitItem' -import { SplitPercentValue } from './SplitPercentValue' - -export function SplitValue({ splitProps }: { splitProps: SplitProps }) { - return ( -
- - {splitProps.showAmount && splitProps.totalValue && splitProps.totalValue > 0n ? ( -
- - - -
- ) : null} -
- ) -} diff --git a/src/packages/v4/components/SplitList/SplitItem/index.tsx b/src/packages/v4/components/SplitList/SplitItem/index.tsx deleted file mode 100644 index 6510985b47..0000000000 --- a/src/packages/v4/components/SplitList/SplitItem/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './SplitItem' diff --git a/src/packages/v4/components/SplitList/SplitList.tsx b/src/packages/v4/components/SplitList/SplitList.tsx deleted file mode 100644 index b29d4380f2..0000000000 --- a/src/packages/v4/components/SplitList/SplitList.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { JBSplit } from 'juice-sdk-core' -import { - sortSplits, - v4GetProjectOwnerRemainderSplit, -} from 'packages/v4/utils/v4Splits' -import { useMemo } from 'react' -import { Hash } from 'viem' -import { SplitItem, SplitProps } from './SplitItem' -export type SplitListProps = { - splits: JBSplit[] - currency?: bigint - totalValue: bigint | undefined - projectOwnerAddress: string | undefined - showAmounts?: boolean - showFees?: boolean - valueSuffix?: string | JSX.Element - valueFormatProps?: { precision?: number } - reservedPercent?: number - dontApplyFeeToAmounts?: boolean -} - -export default function SplitList({ - splits, - showAmounts = false, - showFees = false, - currency, - totalValue, - projectOwnerAddress, - valueSuffix, - valueFormatProps, - reservedPercent, - dontApplyFeeToAmounts, -}: SplitListProps) { - const ownerSplit = useMemo(() => { - if (!projectOwnerAddress) return - return v4GetProjectOwnerRemainderSplit(projectOwnerAddress as Hash, splits) - }, [projectOwnerAddress, splits]) - - const splitProps: Omit = { - currency, - totalValue, - projectOwnerAddress, - valueSuffix, - valueFormatProps, - reservedPercent, - showFee: showFees, - showAmount: showAmounts, - dontApplyFeeToAmount: dontApplyFeeToAmounts, - } - - return ( -
- {sortSplits(splits).map(split => { - return ( - - ) - })} - {ownerSplit && ownerSplit.percent.toFloat() > 0 ? ( - - ) : null} -
- ) -} diff --git a/src/packages/v4/components/V4ProjectHandleLink.tsx b/src/packages/v4/components/V4ProjectHandleLink.tsx index eea594b52a..1af6f3cd91 100644 --- a/src/packages/v4/components/V4ProjectHandleLink.tsx +++ b/src/packages/v4/components/V4ProjectHandleLink.tsx @@ -10,21 +10,19 @@ import { v4ProjectRoute } from 'utils/routes' */ export default function V4ProjectHandleLink({ className, - containerClassName, name, projectId, chainName, withProjectAvatar = false, }: { className?: string - containerClassName?: string name?: string | null chainName: string projectId: number withProjectAvatar?: boolean }) { return ( -
+ <> {withProjectAvatar ? ( {chainName} Project #{projectId} -
+ ) } diff --git a/src/packages/v4/components/V4TokenHoldersModal.tsx b/src/packages/v4/components/V4TokenHoldersModal.tsx deleted file mode 100644 index ab9cf07315..0000000000 --- a/src/packages/v4/components/V4TokenHoldersModal.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber' -import ParticipantsModal from 'components/modals/ParticipantsModal' -import useNameOfERC20 from 'hooks/ERC20/useNameOfERC20' -import { useReadJbTokensTokenOf } from 'juice-sdk-react' -import { useV4TotalTokenSupply } from '../hooks/useV4TotalTokenSupply' - -export const V4TokenHoldersModal = ({ - open, - onClose, -}: { - open: boolean - onClose: VoidFunction -}) => { - const { data: tokenAddress } = useReadJbTokensTokenOf() - const { data: tokenSymbol } = useNameOfERC20(tokenAddress) - - const { data: totalTokenSupply } = useV4TotalTokenSupply() - return ( - - ) -} diff --git a/src/packages/v4/contexts/RulesetCountdownProvider.tsx b/src/packages/v4/contexts/RulesetCountdownProvider.tsx index bf897cdedf..3814f84a62 100644 --- a/src/packages/v4/contexts/RulesetCountdownProvider.tsx +++ b/src/packages/v4/contexts/RulesetCountdownProvider.tsx @@ -25,6 +25,7 @@ export const RulesetCountdownProvider = ({ const endEpochSeconds = ruleset ? Number(ruleset.start + ruleset.duration) : 0 + const { remainingTimeText, secondsRemaining } = useCountdownClock(endEpochSeconds) diff --git a/src/packages/v4/graphql/queries/payEvents.graphql b/src/packages/v4/graphql/queries/payEvents.graphql index 87a9c83495..1386cf1d51 100644 --- a/src/packages/v4/graphql/queries/payEvents.graphql +++ b/src/packages/v4/graphql/queries/payEvents.graphql @@ -14,7 +14,6 @@ query PayEvents( ) { id amount - amountUSD beneficiary note timestamp diff --git a/src/packages/v4/hooks/useJBQueuedRuleset.ts b/src/packages/v4/hooks/useJBQueuedRuleset.ts new file mode 100644 index 0000000000..fe99c6ce15 --- /dev/null +++ b/src/packages/v4/hooks/useJBQueuedRuleset.ts @@ -0,0 +1,23 @@ +import { DecayRate, RulesetWeight } from 'juice-sdk-core'; +import { useReadJbRulesetsLatestQueuedOf } from 'juice-sdk-react'; +import { Ruleset } from '../models/ruleset'; + +export function useJBQueuedRuleset(): { data: Ruleset | undefined } { + const { data } = useReadJbRulesetsLatestQueuedOf(); + const _latestQueuedRuleset = data?.[0]; + + const queuedWeight = new RulesetWeight(_latestQueuedRuleset?.weight ?? 0n) + const queuedDecayRate = new DecayRate(_latestQueuedRuleset?.decayRate ?? 0n) + + const latestQueuedRuleset = _latestQueuedRuleset + ? { + ..._latestQueuedRuleset, + weight: queuedWeight, + decayRate: queuedDecayRate, + } + : undefined; + + return { + data: latestQueuedRuleset, + }; +} diff --git a/src/packages/v4/hooks/useJBUpcomingRuleset.ts b/src/packages/v4/hooks/useJBUpcomingRuleset.ts deleted file mode 100644 index c9030d367f..0000000000 --- a/src/packages/v4/hooks/useJBUpcomingRuleset.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { DecayPercent, JBRulesetData, JBRulesetMetadata, RedemptionRate, ReservedPercent, RulesetWeight } from 'juice-sdk-core'; -import { useJBContractContext, useReadJbControllerUpcomingRulesetOf } from 'juice-sdk-react'; - -// @todo: add to SDK -export function useJBUpcomingRuleset(): { - ruleset: JBRulesetData | undefined, - rulesetMetadata: JBRulesetMetadata | undefined, - isLoading: boolean - } { - const { contracts, projectId } = useJBContractContext() - const { data, isLoading } = useReadJbControllerUpcomingRulesetOf({ - address: contracts.controller?.data ?? undefined, - args: [projectId] - }) - const _latestUpcomingRuleset = data?.[0] - const _latestUpcomingRulesetMetadata = data?.[1] - const upcomingWeight = new RulesetWeight(_latestUpcomingRuleset?.weight ?? 0n) - const upcomingDecayPercent = new DecayPercent(_latestUpcomingRuleset?.decayPercent ?? 0) - - const latestUpcomingRuleset = _latestUpcomingRuleset - ? { - ..._latestUpcomingRuleset, - weight: upcomingWeight, - decayPercent: upcomingDecayPercent, - } - : undefined; - - const upcomingReservedPercent = new ReservedPercent(_latestUpcomingRulesetMetadata?.reservedPercent ?? 0) - const upcomingRedemptionRate = new RedemptionRate(_latestUpcomingRulesetMetadata?.redemptionRate ?? 0) - const latestUpcomingRulesetMetadata = _latestUpcomingRulesetMetadata ? - { - ..._latestUpcomingRulesetMetadata, - reservedPercent: upcomingReservedPercent, - redemptionRate: upcomingRedemptionRate, - - } : undefined - - return { - ruleset: latestUpcomingRuleset, - rulesetMetadata: latestUpcomingRulesetMetadata, - isLoading - }; -} diff --git a/src/packages/v4/hooks/usePayoutLimit.ts b/src/packages/v4/hooks/usePayoutLimits.ts similarity index 51% rename from src/packages/v4/hooks/usePayoutLimit.ts rename to src/packages/v4/hooks/usePayoutLimits.ts index c1ab6c1abe..c11920f456 100644 --- a/src/packages/v4/hooks/usePayoutLimit.ts +++ b/src/packages/v4/hooks/usePayoutLimits.ts @@ -6,30 +6,30 @@ import { V4CurrencyOption } from '../models/v4CurrencyOption'; /** * @todo add to sdk */ -export function usePayoutLimit() { +export function usePayoutLimits() { const { projectId, - contracts: { primaryNativeTerminal, fundAccessLimits }, + contracts: { primaryNativeTerminal: _primaryNativeTerminal }, } = useJBContractContext(); const { data: ruleset } = useJBRuleset(); + const primaryNativeTerminal = _primaryNativeTerminal.data; - const { data: payoutLimits, isLoading } = useReadJbFundAccessLimitsPayoutLimitsOf({ - address: fundAccessLimits.data ?? undefined, + const payoutLimits = useReadJbFundAccessLimitsPayoutLimitsOf({ args: [ projectId, - BigInt(ruleset?.id ?? 0), - primaryNativeTerminal.data ?? constants.AddressZero, + ruleset?.id ?? 0n, + primaryNativeTerminal ?? constants.AddressZero, NATIVE_TOKEN, ], }); - const payoutLimit = payoutLimits?.[0]; + const payoutLimit = payoutLimits?.data?.[0]; return { - data: payoutLimit ? { + data: { ...payoutLimit, - currency: Number(payoutLimit.currency) as V4CurrencyOption, - }: undefined, - isLoading + currency: payoutLimit?.currency as V4CurrencyOption | undefined, + }, + isLoading: payoutLimits?.isLoading, }; } diff --git a/src/packages/v4/hooks/useQueuedPayoutLimits.ts b/src/packages/v4/hooks/useQueuedPayoutLimits.ts new file mode 100644 index 0000000000..3ceb8cd06d --- /dev/null +++ b/src/packages/v4/hooks/useQueuedPayoutLimits.ts @@ -0,0 +1,37 @@ +import * as constants from '@ethersproject/constants'; +import { NATIVE_TOKEN } from 'juice-sdk-core'; +import { useJBContractContext, useReadJbFundAccessLimitsPayoutLimitsOf, useReadJbRulesetsLatestQueuedOf } from 'juice-sdk-react'; +import { V4CurrencyOption } from '../models/v4CurrencyOption'; + +/** + * @todo add to sdk + */ +export function useQueuedPayoutLimits() { + const { + projectId, + contracts: { primaryNativeTerminal: _primaryNativeTerminal }, + } = useJBContractContext() + + const { data: _latestQueuedRuleset } = useReadJbRulesetsLatestQueuedOf(); + + const primaryNativeTerminal = _primaryNativeTerminal.data; + + const latestQueuedRuleset = _latestQueuedRuleset?.[0]; + + const queuedPayoutLimits = useReadJbFundAccessLimitsPayoutLimitsOf({ + args: [ + projectId, + latestQueuedRuleset?.id ?? 0n, + primaryNativeTerminal ?? constants.AddressZero, + NATIVE_TOKEN, + ] + }); + const queuedPayoutLimit = queuedPayoutLimits?.data?.[0] + return { + data: { + ...queuedPayoutLimit, + currency: queuedPayoutLimit?.currency as V4CurrencyOption | undefined, + }, + isLoading: queuedPayoutLimits?.isLoading, + }; +} diff --git a/src/packages/v4/hooks/useUpcomingPayoutLimit.ts b/src/packages/v4/hooks/useUpcomingPayoutLimit.ts deleted file mode 100644 index d00e58bf60..0000000000 --- a/src/packages/v4/hooks/useUpcomingPayoutLimit.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as constants from '@ethersproject/constants'; -import { NATIVE_TOKEN } from 'juice-sdk-core'; -import { useJBContractContext, useReadJbFundAccessLimitsPayoutLimitsOf } from 'juice-sdk-react'; -import { V4CurrencyOption } from '../models/v4CurrencyOption'; -import { useJBUpcomingRuleset } from './useJBUpcomingRuleset'; - -/** - * @todo add to sdk - */ -export function useUpcomingPayoutLimit() { - const { - projectId, - contracts: { primaryNativeTerminal, fundAccessLimits }, - } = useJBContractContext() - - const { ruleset: latestUpcomingRuleset } = useJBUpcomingRuleset(); - - const upcomingPayoutLimits = useReadJbFundAccessLimitsPayoutLimitsOf({ - address: fundAccessLimits.data || undefined, - args: [ - projectId, - BigInt(latestUpcomingRuleset?.id ?? 0n), - primaryNativeTerminal.data ?? constants.AddressZero, - NATIVE_TOKEN, - ] - }); - const upcomingPayoutLimit = upcomingPayoutLimits?.data?.[0] - return { - data: { - ...upcomingPayoutLimit, - currency: upcomingPayoutLimit?.currency as V4CurrencyOption | undefined, - }, - isLoading: upcomingPayoutLimits?.isLoading, - }; -} diff --git a/src/packages/v4/hooks/useUsedPayoutLimitOf.ts b/src/packages/v4/hooks/useUsedPayoutLimitOf.ts deleted file mode 100644 index 5c8be280af..0000000000 --- a/src/packages/v4/hooks/useUsedPayoutLimitOf.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NATIVE_CURRENCY_ID, NATIVE_TOKEN } from 'juice-sdk-core'; -import { - useJBContractContext, - useJBRulesetContext, - useJBTerminalContext, - useReadJbTerminalStoreUsedPayoutLimitOf, -} from 'juice-sdk-react'; -import { zeroAddress } from 'viem'; - -export const useUsedPayoutLimitOf = () => { - const { store } = useJBTerminalContext(); - const { projectId, contracts } = useJBContractContext(); - const { ruleset } = useJBRulesetContext(); - - const { data: usedPayoutLimit, isLoading } = useReadJbTerminalStoreUsedPayoutLimitOf({ - address: store.data ?? undefined, - args: [ - contracts.primaryNativeTerminal.data ?? zeroAddress, - projectId, - NATIVE_TOKEN, - BigInt(ruleset.data?.cycleNumber ?? 0), - NATIVE_CURRENCY_ID, - ], - }); - - return { data: usedPayoutLimit, isLoading }; -}; diff --git a/src/packages/v4/hooks/useV4BalanceOfNativeTerminal.ts b/src/packages/v4/hooks/useV4BalanceOfNativeTerminal.ts deleted file mode 100644 index ec32f2a489..0000000000 --- a/src/packages/v4/hooks/useV4BalanceOfNativeTerminal.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NATIVE_TOKEN } from 'juice-sdk-core'; -import { - useJBContractContext, - useJBTerminalContext, - useReadJbTerminalStoreBalanceOf, -} from 'juice-sdk-react'; -import { zeroAddress } from 'viem'; - -export const useV4BalanceOfNativeTerminal = () => { - const { store } = useJBTerminalContext(); - const { projectId, contracts } = useJBContractContext(); - - const { data: treasuryBalance, isLoading } = useReadJbTerminalStoreBalanceOf({ - address: store.data ?? undefined, - args: [ - contracts.primaryNativeTerminal.data ?? zeroAddress, - projectId, - NATIVE_TOKEN, - ], - }); - - return { data: treasuryBalance, isLoading }; -}; diff --git a/src/packages/v4/hooks/useV4PayoutSplits.ts b/src/packages/v4/hooks/useV4PayoutSplits.ts deleted file mode 100644 index 41708a6b30..0000000000 --- a/src/packages/v4/hooks/useV4PayoutSplits.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { JBSplit, SplitPortion } from 'juice-sdk-core' -import { - useJBContractContext, - useJBRuleset, - useReadJbSplitsSplitsOf, - useReadJbTokensTokenOf, -} from 'juice-sdk-react' - -export const useV4CurrentPayoutSplits = () => { - const { projectId } = useJBContractContext() - const { data: tokenAddress } = useReadJbTokensTokenOf() - const { data: ruleset } = useJBRuleset() - - const groupId = BigInt(tokenAddress ?? 0) // contracts say this is: `uint256(uint160(tokenAddress))` - const { data: _splits, isLoading: currentSplitsLoading } = - useReadJbSplitsSplitsOf({ - args: [projectId, BigInt(ruleset?.id ?? 0), groupId], - query: { - select(data) { - return data.map(d => ({ - ...d, - percent: new SplitPortion(d.percent), - })) - }, - }, - }) - - const splits: JBSplit[] = _splits ? [..._splits] : [] - - return { splits, isLoading: currentSplitsLoading } -} diff --git a/src/packages/v4/hooks/useV4ProjectOwnerOf.ts b/src/packages/v4/hooks/useV4ProjectOwnerOf.ts deleted file mode 100644 index 3be58bae7e..0000000000 --- a/src/packages/v4/hooks/useV4ProjectOwnerOf.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useJBContractContext, useReadJbProjectsOwnerOf } from "juice-sdk-react"; - -const useProjectOwnerOf = () => { - const { projectId } = useJBContractContext(); - - const { data: projectOwnerAddress, isLoading } = useReadJbProjectsOwnerOf({ - args: [projectId], - }); - - return { - data: projectOwnerAddress, - isLoading - }; -}; - -export default useProjectOwnerOf; diff --git a/src/packages/v4/hooks/useV4ReservedSplits.ts b/src/packages/v4/hooks/useV4ReservedSplits.ts deleted file mode 100644 index 800586ddee..0000000000 --- a/src/packages/v4/hooks/useV4ReservedSplits.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { JBSplit, SplitPortion } from 'juice-sdk-core' -import { - useJBContractContext, - useJBRuleset, - useReadJbSplitsSplitsOf, -} from 'juice-sdk-react' - -const RESERVED_SPLITS_GROUP_ID = 1n - -export const useV4ReservedSplits = () => { - const { projectId } = useJBContractContext() - const { data: ruleset } = useJBRuleset() - - const { data: _splits, isLoading: currentSplitsLoading } = - useReadJbSplitsSplitsOf({ - args: [projectId, BigInt(ruleset?.id ?? 0), RESERVED_SPLITS_GROUP_ID], - query: { - select(data) { - return data.map(d => ({ - ...d, - percent: new SplitPortion(d.percent), - })) - }, - }, - }) - - const splits: JBSplit[] = _splits ? [..._splits] : [] - - return { splits, isLoading: currentSplitsLoading } -} diff --git a/src/packages/v4/hooks/useV4TotalTokenSupply.ts b/src/packages/v4/hooks/useV4TotalTokenSupply.ts deleted file mode 100644 index e7df0b9bd4..0000000000 --- a/src/packages/v4/hooks/useV4TotalTokenSupply.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useJBContractContext, useReadJbControllerTotalTokenSupplyWithReservedTokensOf } from 'juice-sdk-react'; - -export const useV4TotalTokenSupply = () => { - const { projectId, contracts: { controller } } = useJBContractContext(); - - const { data: totalTokenSupplyWei, isLoading } = useReadJbControllerTotalTokenSupplyWithReservedTokensOf({ - address: controller.data ?? undefined, - args: [projectId], - }); - - return { - data: totalTokenSupplyWei, - isLoading - }; -}; diff --git a/src/packages/v4/hooks/useV4WalletHasPermission.ts b/src/packages/v4/hooks/useV4WalletHasPermission.ts index 866b46b1d1..e366409fcc 100644 --- a/src/packages/v4/hooks/useV4WalletHasPermission.ts +++ b/src/packages/v4/hooks/useV4WalletHasPermission.ts @@ -1,9 +1,8 @@ import * as constants from '@ethersproject/constants' import { useWallet } from 'hooks/Wallet' -import { useJBContractContext, useReadJbPermissionsHasPermissions } from 'juice-sdk-react' +import { useJBContractContext, useReadJbPermissionsHasPermissions, useReadJbProjectsOwnerOf } from 'juice-sdk-react' import { isEqualAddress } from 'utils/address' import { V4OperatorPermission } from '../models/v4Permissions' -import useProjectOwnerOf from './useV4ProjectOwnerOf' export function useV4WalletHasPermission( permission: V4OperatorPermission | V4OperatorPermission[], @@ -11,7 +10,9 @@ export function useV4WalletHasPermission( const { userAddress } = useWallet() const { projectId } = useJBContractContext() - const { data: projectOwnerAddress } = useProjectOwnerOf() + const { data: projectOwnerAddress } = useReadJbProjectsOwnerOf({ + args: [projectId], + }) const _operator = userAddress ?? constants.AddressZero const _account = projectOwnerAddress ?? constants.AddressZero diff --git a/src/packages/v4/models/ruleset.ts b/src/packages/v4/models/ruleset.ts new file mode 100644 index 0000000000..30c5ef4c52 --- /dev/null +++ b/src/packages/v4/models/ruleset.ts @@ -0,0 +1,16 @@ +import { DecayRate, RulesetWeight } from "juice-sdk-core"; + +export type Ruleset = Omit<{ + cycleNumber: bigint; + id: bigint; + basedOnId: bigint; + start: bigint; + duration: bigint; + weight: bigint; + decayRate: bigint; + approvalHook: `0x${string}`; + metadata: bigint; +}, "weight" | "decayRate"> & { + weight: RulesetWeight; + decayRate: DecayRate; +} diff --git a/src/packages/v4/utils/approvalHooks.ts b/src/packages/v4/utils/approvalHooks.ts index 5e47611ad9..33da693154 100644 --- a/src/packages/v4/utils/approvalHooks.ts +++ b/src/packages/v4/utils/approvalHooks.ts @@ -6,6 +6,6 @@ export const getApprovalStrategyByAddress = (address: string) => { return { address, - name: '1 day (@todo: address->name)' + name: '1 day (@todo)' }; } diff --git a/src/packages/v4/utils/distributions.ts b/src/packages/v4/utils/distributions.ts deleted file mode 100644 index d1add0d6ad..0000000000 --- a/src/packages/v4/utils/distributions.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { BigNumber } from 'ethers' - -import { ONE_BILLION } from 'constants/numbers' -import { JBSplit as Split, SplitPortion, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' -import { fromWad, parseWad } from 'utils/format/formatNumber' -import { isInfinitePayoutLimit } from './fundingCycle' -import { - MAX_PAYOUT_LIMIT, -} from './math' -import { splitPortionFromFormattedPercent } from './v4Splits' - -export const JB_FEE = 0.025 - -/** - * Derive payout amount after the fee has been applied. - * @param amount - Amount before fee applied - * @returns Amount @amount minus the JB fee - */ -export function deriveAmountAfterFee(amount: number) { - return amount - amount * JB_FEE -} - -/** - * Derive payout amount before the fee has been applied. - * @param amount - An amount that has already had the fee applied - * @returns Amount @amount plus the JB fee - */ -export function deriveAmountBeforeFee(amount: number) { - return amount / (1 - JB_FEE) -} - -/** - * Derive payout amount from its % of the distributionLimit. Apply fee if necessary - * @param split - Payout split - * @param distributionLimit - Distribution limit - * @returns Amount that payout will receive as a number. - */ -export function derivePayoutAmount({ - payoutSplit, - distributionLimit, - dontApplyFee, -}: { - payoutSplit: Split - distributionLimit: number | undefined - dontApplyFee?: boolean -}) { - if (!distributionLimit) return 0 - const amountBeforeFee = - (payoutSplit.percent.toFloat() / ONE_BILLION) * distributionLimit - if (isJuiceboxProjectSplit(payoutSplit) || dontApplyFee) return amountBeforeFee // projects dont have fee applied - return deriveAmountAfterFee(amountBeforeFee) -} - -/** - * Gets amount from percent of a bigger amount - * @param percent {float} - value as a percentage. - * @param amount string (hexString) - * @returns {number} distribution amount - */ -export function amountFromPercent({ - percent, - amount, -}: { - percent: number - amount: string -}) { - return (percent / 100) * parseFloat(amount) -} - -/** - * Gets split percent from split amount and the distribution limit - * @param amount {float} - value as a percentage. - * @param distributionLimit number - * @returns {number} percent as an actual percentage of distribution limit (/100) - */ -export function getDistributionPercentFromAmount({ - amount, // Distribution amount before fee - distributionLimit, -}: { - amount: number - distributionLimit: number -}) { - return Number(splitPortionFromFormattedPercent((amount / distributionLimit) * 100)) -} - -/** - * Calculates sum of all split percentages - * @param splits {Split[]} - list of splits - * @returns {number} sum of all split percentanges - */ -export function getTotalSplitsPercentage(splits: Split[]) { - return splits.reduce( - (acc, curr) => acc + curr.percent.formatPercentage(), - 0, - ) -} - -/** - * Due to limitations of rounding errors, it's possible that adjustedSplitPercents causes - * the splits to sum to 99.99999999% or 100.0000001% (causes error) instead of 100%. - * This function does one final pass of the percents to ensure they sum to 100%. - * @param splits {Split[]} - list of current splits to possibly have their percents adjusted - * @returns {Split[]} splits with their percents adjusted - */ -export function ensureSplitsSumTo100Percent({ - splits, -}: { - splits: Split[] -}): Split[] { - // Calculate the percent total of the splits - const currentTotal = splits.reduce((sum, split) => sum + split.percent.toFloat(), 0) - // If the current total is already equal to SPLITS_TOTAL_PERCENT, no adjustment needed - if (currentTotal === SPLITS_TOTAL_PERCENT) { - return splits - } - - // Calculate the ratio to adjust each split by - const ratio = SPLITS_TOTAL_PERCENT / currentTotal - - // Adjust each split - const adjustedSplits = splits.map(split => ({ - ...split, - percent: new SplitPortion(Math.round(split.percent.toFloat() * ratio)), - })) - - // Calculate the total after adjustment - const adjustedTotal = adjustedSplits.reduce( - (sum, split) => sum + split.percent.toFloat(), - 0, - ) - if (adjustedTotal === SPLITS_TOTAL_PERCENT) { - return adjustedSplits - } - - // If there's STILL a difference due to rounding errors, adjust the largest split - const difference = SPLITS_TOTAL_PERCENT - adjustedTotal - const largestSplitIndex = adjustedSplits.findIndex( - split => split.percent.toFloat() === Math.max(...adjustedSplits.map(s => s.percent.toFloat())), - ) - if (adjustedSplits[largestSplitIndex]) { - adjustedSplits[largestSplitIndex].percent = new SplitPortion(adjustedSplits[largestSplitIndex].percent.toFloat() + difference) - } - - return adjustedSplits -} - -/** - * Adjusts exist split percents to stay the same amount when distribution limit is changed - * @param splits {Split[]} - list of current splits to have their percents adjusted - * @param oldDistributionLimit {string} - string of the old distribution limit number (e.g. '1') - * @param newDistributionLimit {string} - string of the new distribution limit number - * @returns {Split[]} splits with their percents adjusted - */ -export function adjustedSplitPercents({ - splits, - oldDistributionLimit, - newDistributionLimit, -}: { - splits: Split[] - oldDistributionLimit: string - newDistributionLimit: string -}) { - const adjustedSplits: Split[] = [] - splits.forEach((split: Split) => { - const currentAmount = amountFromPercent({ - percent: split.percent.formatPercentage(), - amount: oldDistributionLimit, - }) - - const newPercent = getDistributionPercentFromAmount({ - amount: currentAmount, - distributionLimit: parseFloat(newDistributionLimit), - }) - const adjustedSplit = { - beneficiary: split.beneficiary, - percent: new SplitPortion(newPercent), - preferAddToBalance: split.preferAddToBalance, - lockedUntil: split.lockedUntil, - projectId: split.projectId, - hook: split.hook, - } as Split - adjustedSplits?.push(adjustedSplit) - }) - return adjustedSplits -} - -/** - * Derives the new distribution limit when a split amount is altered or added - * @param editingSplitPercent {number} - percent of the split being edited (0 if adding a split) - * @param newDistributionLimit {string} - string of the new distribution limit number (e.g. '1') - * @returns {number} newDistributionLimit - */ -export function getNewDistributionLimit({ - editingSplitPercent, - newSplitAmount, - currentDistributionLimit, - ownerRemainingAmount, -}: { - editingSplitPercent: number // percent per billion - newSplitAmount: number - currentDistributionLimit: string - ownerRemainingAmount?: number -}) { - const previousSplitAmount = - currentDistributionLimit === '0' - ? 0 - : amountFromPercent({ - percent: new SplitPortion(editingSplitPercent).formatPercentage(), - amount: currentDistributionLimit, - }) // will be 0 when adding split but an actual amount when reconfiging or deleting - - return ( - parseFloat(currentDistributionLimit) - - previousSplitAmount + - newSplitAmount - - (ownerRemainingAmount ?? 0) - ) -} - -// Determines if a split is a Juicebox project -export function isJuiceboxProjectSplit(split: Split) { - return split.projectId ? BigNumber.from(split.projectId).gt(0) : false -} - -/** - * Converts the distribution limit that comes from redux from string to a number. If infinite, returns undefined. - * @param distributionLimit {string | undefined} - The distribution limit as a string (or undefined). - * @returns {number | undefined} - Returns the distribution limit as a number if it isn't infinite, otherwise returns undefined. - */ -export function distributionLimitStringtoNumber( - distributionLimit: string | undefined, -) { - if (distributionLimit === undefined) return undefined - const distributionLimitBN = parseWad(distributionLimit) - const distributionLimitIsInfinite = - !distributionLimitBN || distributionLimitBN.eq(MAX_PAYOUT_LIMIT) - return distributionLimitIsInfinite - ? undefined - : parseFloat(fromWad(distributionLimitBN)) -} - -/** - * Determines if two distributionLimits are the same - * @param distributionLimit1 {BigNumber | undefined} - First DL to compare (undefined === unlimited) - * @param distributionLimit2 {BigNumber | undefined} - Second DL to compare (undefined === unlimited) - - * @returns {boolean} - True if DLs are the same, - */ -export function distributionLimitsEqual( - distributionLimit1: bigint | undefined, - distributionLimit2: bigint | undefined, -) { - if ( - isInfinitePayoutLimit(distributionLimit1) && - isInfinitePayoutLimit(distributionLimit2) - ) { - return true - } - return distributionLimit1 === distributionLimit2 -} diff --git a/src/packages/v4/utils/formatCurrencyAmount.ts b/src/packages/v4/utils/formatV4CurrencyAmount.ts similarity index 100% rename from src/packages/v4/utils/formatCurrencyAmount.ts rename to src/packages/v4/utils/formatV4CurrencyAmount.ts diff --git a/src/packages/v4/utils/fundingCycle.ts b/src/packages/v4/utils/fundingCycle.ts deleted file mode 100644 index 52c8e8e887..0000000000 --- a/src/packages/v4/utils/fundingCycle.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MAX_PAYOUT_LIMIT } from "./math" - -export function isInfinitePayoutLimit( - payoutLimit: bigint | undefined, -) { - return ( - payoutLimit === undefined || - payoutLimit === MAX_PAYOUT_LIMIT - ) -} - -// Not zero and not infinite -export const isFinitePayoutLimit = ( - payoutLimit: bigint | undefined, -): boolean => { - return Boolean( - payoutLimit && - !(payoutLimit === 0n) && - !isInfinitePayoutLimit(payoutLimit), - ) -} diff --git a/src/packages/v4/utils/math.ts b/src/packages/v4/utils/math.ts index d38d9fa04b..690b67a47b 100644 --- a/src/packages/v4/utils/math.ts +++ b/src/packages/v4/utils/math.ts @@ -1,13 +1,3 @@ import * as constants from '@ethersproject/constants' -import { feeForAmount } from 'utils/math' export const MAX_PAYOUT_LIMIT = constants.MaxUint256.toBigInt() - -export const amountSubFee = ( - amountWad: bigint | undefined, - feePerBillion: bigint | undefined, -): bigint | undefined => { - if (!feePerBillion || !amountWad) return - const feeAmount = feeForAmount(amountWad, feePerBillion) ?? 0n - return amountWad - feeAmount -} diff --git a/src/packages/v4/utils/splitToAllocation.ts b/src/packages/v4/utils/splitToAllocation.ts deleted file mode 100644 index 12651b7e30..0000000000 --- a/src/packages/v4/utils/splitToAllocation.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { JBSplit, SplitPortion } from 'juice-sdk-core' - -import { zeroAddress } from 'viem' -import { AllocationSplit } from '../components/Allocation/Allocation' -import { sanitizeSplit } from './v4Splits' - -const defaultSplit: JBSplit = { - beneficiary: zeroAddress, - percent: new SplitPortion(0), - preferAddToBalance: false, - lockedUntil: 0, - projectId: 0n, - hook: zeroAddress, -} - -export const splitToAllocation = (split: JBSplit): AllocationSplit => { - return { - id: `${split.beneficiary}${ - split.projectId !== undefined && split.projectId !== 0n ? `-${split.projectId}` : '' - }`, - ...split, - percent: split.percent - } -} - -export const allocationToSplit = (allocation: AllocationSplit): JBSplit => { - const a = { - ...defaultSplit, - ...allocation, - percent: allocation.percent, - } - return sanitizeSplit(a) -} diff --git a/src/packages/v4/utils/v4Splits.ts b/src/packages/v4/utils/v4Splits.ts deleted file mode 100644 index 0d85e64b7c..0000000000 --- a/src/packages/v4/utils/v4Splits.ts +++ /dev/null @@ -1,285 +0,0 @@ -import * as constants from '@ethersproject/constants' -import { JBSplit, SplitPortion, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' -import isEqual from 'lodash/isEqual' -import { formatWad } from 'utils/format/formatNumber' -import { Hash } from 'viem' -import { isFinitePayoutLimit } from './fundingCycle' - -/** - * Return a Split object that represents the remaining percentage allocated to the project owner. - * - * In the Juicebox protocol, if the sum of the split percentages is less than 100%, - * the remainder gets allocated to the project owner. - */ -export const v4GetProjectOwnerRemainderSplit = ( - projectOwnerAddress: Hash, - splits: JBSplit[], -): JBSplit & { isProjectOwner: true } => { - const totalSplitPercentage = v4TotalSplitsPercent(splits) - const ownerPercentage = new SplitPortion( - SPLITS_TOTAL_PERCENT - Number(totalSplitPercentage), - ) - - return { - beneficiary: projectOwnerAddress, - percent: ownerPercentage, - lockedUntil: 0, - projectId: 0n, - isProjectOwner: true, - preferAddToBalance: false, - hook: constants.AddressZero, - } -} - -/** - * Returns the sum of each split's percent in a list of splits - * @param splits {Split[]} - list of splits to sum percents - * @returns {bigint} - sum of percents in part-per-billion (max = SPLITS_TOTAL_PERCENT) - */ -export const v4TotalSplitsPercent = (splits: JBSplit[]): bigint => - splits?.reduce((sum, split) => sum + split.percent.value, 0n) ?? 0n - -// - true if the split has been removed (exists in old but not new), -// - 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 - -export type SplitWithDiff = JBSplit & { - oldSplit?: OldSplit -} - -// determines if two splits are the same 'entity' (either projectId or address) -export const hasEqualRecipient = (a: JBSplit, b: JBSplit) => { - const isProject = isProjectSplit(a) || isProjectSplit(b) - const idsEqual = - a.projectId === b.projectId || - BigInt(a.projectId ?? 0) === BigInt(b.projectId ?? 0) || - BigInt(b.projectId ?? 0) === BigInt(a.projectId ?? 0) - - return ( - (isProject && idsEqual) || (!isProject && a.beneficiary === b.beneficiary) - ) -} - -// return list of splits that exist in oldSplits but not newSplits -const getRemovedSplits = (oldSplits: JBSplit[], newSplits: JBSplit[]) => { - return oldSplits.filter(oldSplit => { - return !newSplits.some(newSplit => hasEqualRecipient(oldSplit, newSplit)) - }) -} - -export const sanitizeSplit = (split: JBSplit): JBSplit => { - return { - lockedUntil: split.lockedUntil ?? 0n, - projectId: split.projectId ?? '0x0', - beneficiary: split.beneficiary ?? constants.AddressZero, - hook: split.hook ?? constants.AddressZero, - preferAddToBalance: false, - percent: split.percent, - } -} - -/** - * Converts array of splits from transaction data (e.g. outgoing reconfig tx) to array of native JBSplit objects - * (Outgoing Split objects have percent and lockedUntil as bigints) - */ -export const toSplit = (splits: JBSplit[]): JBSplit[] => { - return ( - splits?.map( - (split: JBSplit) => ({ ...split, percent: split.percent } as JBSplit), - ) ?? [] - ) -} - -// returns a given list of splits sorted by percent allocation -export const sortSplits = (splits: JBSplit[]) => { - return [...splits].sort((a, b) => (a.percent < b.percent ? 1 : -1)) -} - -/* Determines if two splits AMOUNTS are equal. Extracts amounts for two splits from their respective totalValues **/ -function splitAmountsAreEqual({ - split1, - split2, - split1TotalValue, - split2TotalValue, -}: { - split1: JBSplit - split2: JBSplit - split1TotalValue: bigint - split2TotalValue: bigint -}) { - const split1Amount = formatWad( - (split1TotalValue * split1.percent.value) / BigInt(SPLITS_TOTAL_PERCENT), - { - precision: 2, - }, - ) - const split2Amount = formatWad( - (split2TotalValue * split2.percent.value) / BigInt(SPLITS_TOTAL_PERCENT), - { - precision: 2, - }, - ) - return split2Amount === split1Amount -} - -/* Determines if two splits are equal. If given totalValues, uses the amount of each split instead of its percent **/ -function splitsAreEqual({ - split1, - split2, - split1TotalValue, - split2TotalValue, -}: { - split1: JBSplit - split2: JBSplit - split1TotalValue: bigint - split2TotalValue: bigint -}) { - const isFiniteTotalValue = - isFinitePayoutLimit(split1TotalValue) && - isFinitePayoutLimit(split2TotalValue) - if (!isFiniteTotalValue) { - return isEqual(split1, split2) - } - - return ( - splitAmountsAreEqual({ - split1, - split2, - split1TotalValue, - split2TotalValue, - }) && - split1.beneficiary === split2.beneficiary && - split1.hook === split2.hook && - split1.lockedUntil === split2.lockedUntil && - split1.projectId === split2.projectId && - split1.preferAddToBalance === split2.preferAddToBalance - ) -} - -// returns all unique and diffed splits (projectIds or addresses), sorted by their new `percent` -// and assigns each split an additional property `oldSplit` (OldSplit) -export const processUniqueSplits = ({ - oldTotalValue, - newTotalValue, - oldSplits, - newSplits, - allSplitsChanged, -}: { - oldTotalValue?: bigint - newTotalValue?: bigint - oldSplits: JBSplit[] | undefined - newSplits: JBSplit[] - allSplitsChanged?: boolean // pass when you know all splits have changed (e.g. currency has changed) -}): Array< - SplitWithDiff & { - totalValue?: number - oldSplit?: OldSplit & { totalValue?: bigint } - } -> => { - const uniqueSplitsByProjectIdOrAddress: Array< - SplitWithDiff & { - totalValue?: bigint - oldSplit?: OldSplit & { totalValue?: bigint } - } - > = [] - if (!oldSplits) { - return sortSplits(newSplits) - } - - newSplits.map(split => { - const oldSplit = oldSplits.find(oldSplit => - hasEqualRecipient(oldSplit, split), - ) - const splitsEqual = - oldSplit && !allSplitsChanged && newTotalValue && oldTotalValue - ? splitsAreEqual({ - split1: split, - split2: oldSplit, - split1TotalValue: newTotalValue, - split2TotalValue: oldTotalValue, - }) - : false - - if (oldSplit && !splitsEqual) { - // adds diffed splits (exists in new and old and there is diff) - uniqueSplitsByProjectIdOrAddress.push({ - ...split, - oldSplit: { - ...oldSplit, - totalValue: oldTotalValue, - }, - }) - } else if (oldSplit && splitsEqual) { - // undiff'd split (exists in new and old and there is no diff) = DO NOTHING - return - } else { - // adds the new splits (exists in new but not old) - uniqueSplitsByProjectIdOrAddress.push({ - ...split, - oldSplit: false, - }) - } - }) - - // adds the old splits (exists in old but not new) - const removedSplits = getRemovedSplits(oldSplits, newSplits) - removedSplits.map(split => { - uniqueSplitsByProjectIdOrAddress.push({ - ...split, - oldSplit: true, - }) - }) - return sortSplits(uniqueSplitsByProjectIdOrAddress) -} - -export const isProjectSplit = (split: JBSplit): boolean => { - return Boolean(split.projectId) && split.projectId > 0n -} - -/** - * Returns the sum of each split's percent in a list of splits - * @param splits {JBSplit[]} - list of splits to sum percents - * @returns {number} - sum of percents in part-per-billion (max = SPLITS_TOTAL_PERCENT) - */ -export const totalSplitsPercent = (splits: JBSplit[]): number => - splits?.reduce((sum, split) => sum + Number(split.percent), 0) ?? 0 - -/** - * Determines if two lists of splits have any diff's within them. - * @param splits1 {JBSplit[]} - first list of splits - * @param splits2 {JBSplit[]} - second list of splits - * @returns {boolean} - true if splits have a diff, false if the same. - */ -export function splitsListsHaveDiff( - splits1: JBSplit[] | undefined, - splits2: JBSplit[] | undefined, -) { - if (!splits1 && !splits2) return false - if ((splits1 && !splits2) || (!splits1 && splits2)) return true - - for (const split1 of splits1 ?? []) { - const correspondingSplit = splits2?.find(split2 => - hasEqualRecipient(split1, split2), - ) - - if (!correspondingSplit || !isEqual(split1, correspondingSplit)) { - return true - } - } - - return splits1?.length !== splits2?.length -} - -// Determines if a split is a Juicebox project -export function isJuiceboxProjectSplit(split: JBSplit) { - return split.projectId ? split.projectId > 0n : false -} - -// e.g. Converts 10 to 100000000 (10% of SPLITS_TOTAL_PERCENT) -export const splitPortionFromFormattedPercent = (percentage: number): bigint => { - return percentage - ? BigInt((percentage * SPLITS_TOTAL_PERCENT) / 100) - : 0n -} diff --git a/src/packages/v4/views/V4ProjectDashboard/ProjectHeaderStats.tsx b/src/packages/v4/views/V4ProjectDashboard/ProjectHeaderStats.tsx deleted file mode 100644 index 67430d0645..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/ProjectHeaderStats.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber' -import { ArrowTrendingUpIcon } from '@heroicons/react/24/outline' -import { t, Trans } from '@lingui/macro' -import ETHAmount from 'components/currency/ETHAmount' -import { ProjectHeaderStat } from 'components/Project/ProjectHeader/ProjectHeaderStat' -import { TRENDING_WINDOW_DAYS } from 'components/Projects/RankingExplanation' -import { PropsWithChildren, useCallback } from 'react' -import { twMerge } from 'tailwind-merge' -import { useProjectPageQueries } from './hooks/useProjectPageQueries' -import { useV4ProjectHeader } from './hooks/useV4ProjectHeader' - -export function ProjectHeaderStats() { - const { payments, totalVolume, last7DaysPercent } = useV4ProjectHeader() - const { setProjectPageTab } = useProjectPageQueries() - - const openActivityTab = useCallback( - () => setProjectPageTab('activity'), - [setProjectPageTab], - ) - - return ( -
- - - - - } - /> - {last7DaysPercent !== Infinity ? ( - last {TRENDING_WINDOW_DAYS} days} - stat={ - 0 ? 'trending' : 'stagnant'}> - {last7DaysPercent}% - - } - data-testid="project-header-trending-perc" - /> - ) : null} -
- ) -} - -type StatBadgeProps = { - type: 'trending' | 'stagnant' -} - -const StatBadge: React.FC> = ({ - type, - children, -}) => { - return ( -
- - {children} -
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4PayRedeemCard/V4PayRedeemCard.tsx b/src/packages/v4/views/V4ProjectDashboard/V4PayRedeemCard/V4PayRedeemCard.tsx deleted file mode 100644 index 144855e9fe..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4PayRedeemCard/V4PayRedeemCard.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Button } from 'antd' -import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext' -import { useWallet } from 'hooks/Wallet' -import { NATIVE_TOKEN, NATIVE_TOKEN_DECIMALS } from 'juice-sdk-core' -import { - useJBContractContext, - useWriteJbMultiTerminalPay, -} from 'juice-sdk-react' -import { useContext, useState } from 'react' -import { parseUnits } from 'viem' - -// TODO wildly incomplete. Needs full redo -export function V4PayRedeemCard({ className }: { className: string }) { - const [value, setValue] = useState('0.0001') - - const { contracts, projectId } = useJBContractContext() - const { addTransaction } = useContext(TxHistoryContext) - const { userAddress } = useWallet() - - const valuePayload = value ? parseUnits(value, NATIVE_TOKEN_DECIMALS) : 0n - - const { writeContractAsync: writePay } = useWriteJbMultiTerminalPay() - - const onWrite = async () => { - if (!value || !contracts.primaryNativeTerminal.data || !userAddress) { - return - } - - const args = [ - projectId, - NATIVE_TOKEN, - valuePayload, - userAddress, - 0n, - `JBM V4 ${projectId}`, // TODO update - '0x0', - ] as const - - const txHash = await writePay({ - address: contracts.primaryNativeTerminal.data, - args, - value: valuePayload, - }) - - addTransaction?.('Pay', { hash: txHash }) - } - - return ( -
- -
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectDashboard.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectDashboard.tsx index 0e4c9b2b55..124b106c3c 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectDashboard.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectDashboard.tsx @@ -1,10 +1,14 @@ import { CoverPhoto } from 'components/Project/ProjectHeader/CoverPhoto' +import { NativeTokenValue, useJBContractContext, useNativeTokenSurplus } from 'juice-sdk-react' +import { ActivityList } from 'packages/v4/components/ActivityList/ActivitiyList' import { twMerge } from 'tailwind-merge' -import { V4PayRedeemCard } from './V4PayRedeemCard/V4PayRedeemCard' import { V4ProjectHeader } from './V4ProjectHeader' import { V4ProjectTabs } from './V4ProjectTabs/V4ProjectTabs' export function V4ProjectDashboard() { + const { projectId } = useJBContractContext() + const { data: nativeTokenSurplus } = useNativeTokenSurplus() + return ( <>
@@ -19,12 +23,12 @@ export function V4ProjectDashboard() { '[@media(min-width:960px)]:flex [@media(min-width:960px)]:max-w-6xl [@media(min-width:960px)]:justify-between [@media(min-width:960px)]:gap-x-8', )} > - + /> */}
+
+
+
+ Surplus (overflow):{' '} + {nativeTokenSurplus ? ( + + ) : null} +
+
Project Id: {projectId.toString()}
+ +
+ +
+
+ +
+ Pay me +
Input
+
+
) } diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx index 716f5ab625..6dba5295d2 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx @@ -4,11 +4,12 @@ import { Button, Divider } from 'antd' import { Badge } from 'components/Badge' import EthereumAddress from 'components/EthereumAddress' import { GnosisSafeBadge } from 'components/Project/ProjectHeader/GnosisSafeBadge' -import { useSocialLinks } from 'components/Project/ProjectHeader/hooks/useSocialLinks' import { ProjectHeaderLogo } from 'components/Project/ProjectHeader/ProjectHeaderLogo' import { ProjectHeaderPopupMenu } from 'components/Project/ProjectHeader/ProjectHeaderPopupMenu' +import { ProjectHeaderStats } from 'components/Project/ProjectHeader/ProjectHeaderStats' import { SocialLinkButton } from 'components/Project/ProjectHeader/SocialLinkButton' import { Subtitle } from 'components/Project/ProjectHeader/Subtitle' +import { useSocialLinks } from 'components/Project/ProjectHeader/hooks/useSocialLinks' import { TruncatedText } from 'components/TruncatedText' import useMobile from 'hooks/useMobile' import Link from 'next/link' @@ -18,7 +19,6 @@ import { V4OperatorPermission } from 'packages/v4/models/v4Permissions' import { twMerge } from 'tailwind-merge' import { settingsPagePath, v4ProjectRoute } from 'utils/routes' import { useV4ProjectHeader } from './hooks/useV4ProjectHeader' -import { ProjectHeaderStats } from './ProjectHeaderStats' export type SocialLink = 'twitter' | 'discord' | 'telegram' | 'website' diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ConfigurationDisplayCard.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4ConfigurationDisplayCard.tsx similarity index 100% rename from src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ConfigurationDisplayCard.tsx rename to src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4ConfigurationDisplayCard.tsx diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CurrentUpcomingConfigurationPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4CurrentUpcomingConfigurationPanel.tsx similarity index 100% rename from src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CurrentUpcomingConfigurationPanel.tsx rename to src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4CurrentUpcomingConfigurationPanel.tsx diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CurrentUpcomingSubPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4CurrentUpcomingSubPanel.tsx similarity index 66% rename from src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CurrentUpcomingSubPanel.tsx rename to src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4CurrentUpcomingSubPanel.tsx index fcba8b6663..bf0c27cb75 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CurrentUpcomingSubPanel.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4CurrentUpcomingSubPanel.tsx @@ -3,24 +3,15 @@ import { Trans, t } from '@lingui/macro' import { currentCycleRemainingLengthTooltip } from 'components/Project/ProjectTabs/CyclesPayoutsTab/CyclesPanelTooltips' import { UpcomingCycleChangesCallout } from 'components/Project/ProjectTabs/CyclesPayoutsTab/UpcomingCycleChangesCallout' import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard' -import { RulesetCountdownProvider } from 'packages/v4/contexts/RulesetCountdownProvider' +import { useMemo } from 'react' import { twMerge } from 'tailwind-merge' -import { useRulesetCountdown } from '../../hooks/useRulesetCountdown' import { useV4CurrentUpcomingSubPanel } from '../../hooks/useV4CurrentUpcomingSubPanel' -import { useV4UpcomingRulesetHasChanges } from './hooks/useV4UpcomingRulesetHasChanges' import { V4ConfigurationDisplayCard } from './V4ConfigurationDisplayCard' -import { V4PayoutsSubPanel } from './V4PayoutsSubPanel' - -function CountdownClock({ rulesetUnlocked }: { rulesetUnlocked: boolean }) { - const { timeRemainingText } = useRulesetCountdown() - - const remainingTime = rulesetUnlocked ? '-' : timeRemainingText +import { useV4UpcomingRulesetHasChanges } from './hooks/useV4UpcomingRulesetHasChanges' - if (!remainingTime) { - return - } - return <>{remainingTime} -} +const CYCLE_NUMBER_INDEX = 0 +const STATUS_INDEX = 1 +const CYCLE_LENGTH_INDEX = 2 export const V4CurrentUpcomingSubPanel = ({ id, @@ -30,10 +21,42 @@ export const V4CurrentUpcomingSubPanel = ({ const info = useV4CurrentUpcomingSubPanel(id) const { hasChanges, loading } = useV4UpcomingRulesetHasChanges() + const topPanelsInfo = useMemo(() => { + const topPanelInfo = [ + { + title: t`Ruleset #`, + value: info.rulesetNumber, + }, + { + title: t`Status`, + value: info.status, + }, + ] + if (info.type === 'current') { + topPanelInfo.push({ + title: t`Remaining time`, + value: info.remainingTime, + }) + } + if (info.type === 'upcoming') { + topPanelInfo.push({ + title: t`Ruleset length`, + value: info.rulesetLength, + }) + } + return topPanelInfo + }, [ + info.rulesetLength, + info.rulesetNumber, + info.remainingTime, + info.status, + info.type, + ]) + const rulesetLengthTooltip = info.type === 'current' ? currentCycleRemainingLengthTooltip : undefined - const rulesetLengthValue = info.rulesetLength?.toString() + const rulesetLengthValue = topPanelsInfo[CYCLE_LENGTH_INDEX].value const rulesetStatusTooltip = info.currentRulesetUnlocked ? ( The project's rules are unlocked and can change at any time. @@ -91,46 +114,34 @@ export const V4CurrentUpcomingSubPanel = ({
} + title={topPanelsInfo[CYCLE_NUMBER_INDEX].title} + description={ + topPanelsInfo[CYCLE_NUMBER_INDEX].value ?? + } /> + topPanelsInfo[STATUS_INDEX].value ?? } tooltip={rulesetStatusTooltip} /> - - {info.type === 'current' ? ( - - - - } - tooltip={rulesetLengthTooltip} - /> - ) : ( - - } - tooltip={rulesetLengthTooltip} - /> - )} + + ) + } + tooltip={rulesetLengthTooltip} + />
- + {/* */} ) } diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CyclesPayoutsPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4CyclesPayoutsPanel.tsx similarity index 100% rename from src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4CyclesPayoutsPanel.tsx rename to src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/V4CyclesPayoutsPanel.tsx diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4CurrentUpcomingConfigurationPanel.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4CurrentUpcomingConfigurationPanel.ts new file mode 100644 index 0000000000..73ed67dd9c --- /dev/null +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4CurrentUpcomingConfigurationPanel.ts @@ -0,0 +1,18 @@ +import { useV4CycleSection } from './useV4CycleSection' + + +export const useV4CurrentUpcomingConfigurationPanel = ( + type: 'current' | 'upcoming', +) => { + const cycle = useV4CycleSection(type) + // const token = useTokenSection(type) + // const otherRules = useOtherRulesSection(type) + // const extension = useExtensionSection(type) + + return { + cycle, + // token, + // otherRules, + // extension, + } +} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4CycleSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4CycleSection.ts new file mode 100644 index 0000000000..00c9f371ca --- /dev/null +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4CycleSection.ts @@ -0,0 +1,42 @@ +import { ConfigurationPanelTableData } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' +import { useJBRuleset } from 'juice-sdk-react' +import { useJBQueuedRuleset } from 'packages/v4/hooks/useJBQueuedRuleset' +import { usePayoutLimits } from 'packages/v4/hooks/usePayoutLimits' +import { useQueuedPayoutLimits } from 'packages/v4/hooks/useQueuedPayoutLimits' +import { useV4FormatConfigurationCycleSection } from './useV4FormatConfigurationCycleSection' + +export const useV4CycleSection = ( + type: 'current' | 'upcoming', +): ConfigurationPanelTableData => { + const { data: ruleset } = useJBRuleset() + + const { data: queuedRuleset } = useJBQueuedRuleset() + + const { data: payoutLimits } = usePayoutLimits() + const payoutLimitAmount = payoutLimits?.amount + const payoutLimitCurrency = payoutLimits?.currency + + const { data: queuedPayoutLimits } = useQueuedPayoutLimits() + const queuedPayoutLimitAmount = queuedPayoutLimits?.amount + const queuedPayoutLimitCurrency = queuedPayoutLimits?.currency + + return useV4FormatConfigurationCycleSection({ + ruleset, + payoutLimitAmountCurrency: { + amount: payoutLimitAmount, + currency: payoutLimitCurrency, + }, + + queuedRuleset, + upcomingDistributionLimitAmountCurrency: { + distributionLimit: queuedPayoutLimitAmount, + currency: queuedPayoutLimitCurrency, + }, + + // Hide upcoming info from current section. + ...(type === 'current' && { + upcomingFundingCycle: null, + upcomingDistributionLimitAmountCurrency: null, + }), + }) +} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4FormatConfigurationCycleSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4FormatConfigurationCycleSection.ts new file mode 100644 index 0000000000..971c483bbf --- /dev/null +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4FormatConfigurationCycleSection.ts @@ -0,0 +1,127 @@ +import { t } from '@lingui/macro'; +import { pairToDatum } from 'components/Project/ProjectHeader/utils/pairToDatum'; +import { ConfigurationPanelDatum } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel'; +import { Ruleset } from 'packages/v4/models/ruleset'; +import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption'; +import { getApprovalStrategyByAddress } from 'packages/v4/utils/approvalHooks'; +import { formatCurrencyAmount } from 'packages/v4/utils/formatV4CurrencyAmount'; +import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math'; +import { useMemo } from 'react'; +import { formatTime } from 'utils/format/formatTime'; +import { timeSecondsToDateString } from 'utils/timeSecondsToDateString'; + +export const useV4FormatConfigurationCycleSection = ({ + ruleset, + payoutLimitAmountCurrency, + queuedRuleset, + upcomingPayoutLimitAmountCurrency, +}: { + ruleset?: Ruleset | null; + payoutLimitAmountCurrency: { + amount: bigint | undefined; + currency: V4CurrencyOption | undefined; + }; + queuedRuleset?: Ruleset | null; + upcomingPayoutLimitAmountCurrency?: { + amount: bigint | undefined; + currency: V4CurrencyOption | undefined; + } | null; +}) => { + const formatDuration = (duration: bigint | undefined) => { + if (duration === undefined) return undefined; + if (duration === 0n) return t`Not set`; + return timeSecondsToDateString(Number(duration), 'short', 'lower'); + }; + + const durationDatum: ConfigurationPanelDatum = useMemo(() => { + const currentDuration = formatDuration(ruleset?.duration); + if (queuedRuleset === null) { + return pairToDatum(t`Duration`, currentDuration, null); + } + const upcomingDuration = formatDuration(queuedRuleset?.duration); + + return pairToDatum(t`Duration`, currentDuration, upcomingDuration); + }, [ruleset?.duration, queuedRuleset]); + + const queuedRulesetStart = ruleset?.start ? + ruleset.start + (ruleset?.duration || 0n) + : 0n; + + const startTimeDatum: ConfigurationPanelDatum = useMemo(() => { + const formattedTime = + queuedRuleset === null + ? formatTime(ruleset?.start) + : ruleset?.duration === 0n + ? t`Any time` + : formatTime(queuedRulesetStart); + + const formatTimeDatum: ConfigurationPanelDatum = { + name: t`Start time`, + new: formattedTime, + easyCopy: true, + }; + return formatTimeDatum; + }, [ruleset?.start, ruleset?.duration, queuedRuleset, queuedRulesetStart]); + + const formatPayoutAmount = ( + amount: bigint | undefined, + currency: V4CurrencyOption | undefined, + ) => { + if (amount === undefined) return undefined; + if (amount === MAX_PAYOUT_LIMIT) return t`Unlimited`; + if (amount === 0n) return t`Zero (no payouts)`; + return formatCurrencyAmount({ + amount: Number(amount) / 1e18, // Assuming fromWad + currency, + }); + }; + + const payoutsDatum: ConfigurationPanelDatum = useMemo(() => { + const { amount, currency } = payoutLimitAmountCurrency ?? {}; + const currentPayout = formatPayoutAmount(amount, currency); + + if (upcomingPayoutLimitAmountCurrency === null) { + return pairToDatum(t`Payouts`, currentPayout, null); + } + + const upcomingPayoutLimit = + upcomingPayoutLimitAmountCurrency?.amount !== undefined + ? upcomingPayoutLimitAmountCurrency.amount + : undefined; + const upcomingPayoutLimitCurrency = + upcomingPayoutLimitAmountCurrency?.currency !== undefined + ? upcomingPayoutLimitAmountCurrency.currency + : undefined; + const upcomingPayout = formatPayoutAmount( + upcomingPayoutLimit, + upcomingPayoutLimitCurrency, + ); + + return pairToDatum(t`Payouts`, currentPayout, upcomingPayout); + }, [payoutLimitAmountCurrency, upcomingPayoutLimitAmountCurrency]); + + const editDeadlineDatum: ConfigurationPanelDatum = useMemo(() => { + const currentApprovalStrategy = ruleset?.approvalHook + ? getApprovalStrategyByAddress(ruleset.approvalHook) + : undefined; + const current = currentApprovalStrategy?.name; + if (queuedRuleset === null) { + return pairToDatum(t`Edit deadline`, current, null); + } + + const upcomingBallotStrategy = queuedRuleset?.approvalHook + ? getApprovalStrategyByAddress(queuedRuleset.approvalHook) + : undefined; + const upcoming = upcomingBallotStrategy?.name; + return pairToDatum(t`Edit deadline`, current, upcoming); + }, [ruleset?.approvalHook, queuedRuleset]); + + return useMemo(() => { + return { + duration: durationDatum, + startTime: startTimeDatum, + payouts: payoutsDatum, + editDeadline: editDeadlineDatum, + }; + }, [durationDatum, startTimeDatum, editDeadlineDatum, payoutsDatum]); +}; diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4UpcomingRulesetHasChanges.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4UpcomingRulesetHasChanges.ts similarity index 100% rename from src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4UpcomingRulesetHasChanges.ts rename to src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/CyclesPayoutsPanel/hooks/useV4UpcomingRulesetHasChanges.ts diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/ActivityOptions.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/ActivityOptions.tsx deleted file mode 100644 index e7179bd976..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/ActivityOptions.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { ArrowDownTrayIcon } from "@heroicons/react/24/outline" -import { t } from "@lingui/macro" -import { Button } from "antd" -import { ActivityOption, ALL_OPT } from "components/ActivityList" -import { JuiceListbox } from "components/inputs/JuiceListbox" -import { ProjectEventFilter } from "hooks/useProjectEvents" -import { useState } from "react" -import DownloadActivityModal from "./DownloadActivityModal" -import { ActivityEvents } from "./models/ActivityEvents" - -const activityOptions: ActivityOption[] = [ - ALL_OPT(), - { label: t`Paid`, value: 'payEvent' }, - // TODO:: Other events: - // { label: t`Redeemed`, value: 'redeemEvent' }, - // { label: t`Burned`, value: 'burnEvent' }, - // { label: t`Sent payouts`, value: 'distributePayoutsEvent' }, - // { - // label: t`Sent reserved tokens`, - // value: 'distributeReservedTokensEvent', - // }, - // { label: t`Edited cycle`, value: 'configureEvent' }, - // { label: t`Edited payout`, value: 'setFundAccessConstraintsEvent' }, - // { label: t`Transferred ETH to project`, value: 'addToBalanceEvent' }, - // { label: t`Deployed ERC20`, value: 'deployedERC20Event' }, - // { - // label: t`Created a project payer address`, - // value: 'deployETHERC20ProjectPayerEvent', - // }, - // { label: t`Created project`, value: 'projectCreateEvent' }, -] - -export function ActivityOptions({ - events -}: { - events: ActivityEvents -}) { - const [eventFilter, setEventFilter] = useState('all') - - const [downloadModalVisible, setDownloadModalVisible] = useState() - - const activityOption = activityOptions.find(o => o.value === eventFilter) - - const hasAnyEvents = Object.values(events).some(eventArray => eventArray && eventArray.length > 0); - const canDownload = hasAnyEvents - - return ( - <> -
- {canDownload && ( -
- setDownloadModalVisible(false)} - /> - - ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/DownloadActivityModal.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/DownloadActivityModal.tsx deleted file mode 100644 index 15afd5548a..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/DownloadActivityModal.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { ArrowDownTrayIcon } from '@heroicons/react/24/outline' -import { t, Trans } from '@lingui/macro' -import { Button, Modal, ModalProps } from 'antd' -import InputAccessoryButton from 'components/buttons/InputAccessoryButton' -import FormattedNumberInput from 'components/inputs/FormattedNumberInput' -import { useJBContractContext } from 'juice-sdk-react' -import { useEffect, useState } from 'react' -import { useBlockNumber } from 'wagmi' -import { useDownloadPayments } from './hooks/useDownloadPayments' - -export default function DownloadActivityModal(props: ModalProps) { - const [blockNumber, setBlockNumber] = useState() - const { projectId: _projectId } = useJBContractContext() - const projectId = Number(_projectId) - - const { data: latestBlockNumber } = useBlockNumber() - - // Use block number 5 blocks behind chain head to allow for subgraph being a bit behind on indexing. - const adjustedLatestBlockNumber = latestBlockNumber ? Number(latestBlockNumber) - 5 : undefined - - const { downloadPayments } = useDownloadPayments(blockNumber ?? 0, projectId); - - useEffect(() => { - setBlockNumber(adjustedLatestBlockNumber) - }, [adjustedLatestBlockNumber]) - - return ( - Download project activity CSVs} - {...props} - > - - - setBlockNumber(val ? parseInt(val) : undefined)} - accessory={ - setBlockNumber(adjustedLatestBlockNumber)} - disabled={blockNumber === adjustedLatestBlockNumber} - /> - } - className="mb-0" - max={adjustedLatestBlockNumber} - /> - -
- {/* */} - - {/* */} - - - - {/* - - */} -
-
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/V4ActivityList.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/V4ActivityList.tsx deleted file mode 100644 index b12f0ea608..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/V4ActivityList.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { t } from '@lingui/macro' -import { ActivityEvent } from 'components/activityEventElems/ActivityElement/ActivityElement' -import Loading from 'components/Loading' -import { NativeTokenValue, useJBContractContext, useJBTokenContext } from 'juice-sdk-react' -import { - OrderDirection, - PayEvent_OrderBy, - PayEventsDocument -} from 'packages/v4/graphql/client/graphql' -import { useSubgraphQuery } from 'packages/v4/graphql/useSubgraphQuery' -import { ActivityOptions } from './ActivityOptions' -import { PayEvent } from './models/ActivityEvents' -import { transformPayEventsRes } from './utils/transformEventsData' - - -export function V4ActivityList() { - const { token } = useJBTokenContext() - const { projectId } = useJBContractContext() - - // TODO: pageSize (pagination) - const { data: payEventsData, isLoading } = useSubgraphQuery(PayEventsDocument, { - orderBy: PayEvent_OrderBy.timestamp, - orderDirection: OrderDirection.desc, - where: { - projectId: Number(projectId), - }, - }) - - const payEvents = transformPayEventsRes(payEventsData) ?? [] - - if (!token?.data?.symbol) return null - return ( -
-
-

Activity

- -
-
- {isLoading && } - {isLoading || (payEvents && payEvents.length > 0) ? ( - payEvents?.map((event: PayEvent) => { - return ( -
- - - - } - extra={ - - bought {event.beneficiaryTokenCount?.format(6)} {token.data?.symbol} - - } - /> -
- ) - }) - ) : ( - No activity yet. - )} -
-
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/V4ActivityPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/V4ActivityPanel.tsx deleted file mode 100644 index 311748a86c..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/V4ActivityPanel.tsx +++ /dev/null @@ -1,43 +0,0 @@ - -import { Trans } from '@lingui/macro' -import { ErrorBoundaryCallout } from 'components/Callout/ErrorBoundaryCallout' -import Loading from 'components/Loading' -import VolumeChart from 'components/VolumeChart' -import { PV_V4 } from 'constants/pv' -import { useJBContractContext } from 'juice-sdk-react' -import { ProjectsDocument } from 'packages/v4/graphql/client/graphql' -import { useSubgraphQuery } from 'packages/v4/graphql/useSubgraphQuery' -import { Suspense } from 'react' -import { V4ActivityList } from './V4ActivityList' - -export function V4ActivityPanel() { - const { projectId } = useJBContractContext() - const { data } = useSubgraphQuery(ProjectsDocument, { - where: { - projectId: Number(projectId), - }, - }) - const createdAt = data?.projects?.[0].createdAt - - return ( -
- {Boolean(projectId) && ( -
- }> - Volume chart failed to load.} - > - - - -
- )} - -
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/hooks/useDownloadPayments.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/hooks/useDownloadPayments.ts deleted file mode 100644 index 0ef53cd166..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/hooks/useDownloadPayments.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { t } from "@lingui/macro"; -import { OrderDirection, PayEvent_OrderBy, PayEventsDocument } from "packages/v4/graphql/client/graphql"; -import { useSubgraphQuery } from "packages/v4/graphql/useSubgraphQuery"; -import { useCallback, useState } from 'react'; -import { downloadCsvFile } from "utils/csv"; -import { emitErrorNotification } from 'utils/notifications'; -import { transformPayEventsRes } from "../utils/transformEventsData"; - - -export const useDownloadPayments = (blockNumber: number, projectId: number) => { - const [isLoading, setIsLoading] = useState(false); - - const { data: payEventsData } = useSubgraphQuery(PayEventsDocument, { - orderBy: PayEvent_OrderBy.timestamp, - orderDirection: OrderDirection.desc, - where: { - projectId: Number(projectId), - }, - }); - - const downloadPayments = useCallback(async () => { - if (blockNumber === undefined || !projectId) return; - - setIsLoading(true); - - const rows = [ - [ - t`Date`, - t`ETH paid`, - // t`USD value of ETH paid`, //TODO: not working for V4 (check subgraph - t`Payer`, - t`Transaction hash`, - ], // CSV header row - ]; - - try { - const payEvents = transformPayEventsRes(payEventsData); - - if (!payEvents) { - emitErrorNotification(t`Error loading payouts`); - throw new Error('No data.'); - } - - // Interpolate distributions into payouts. - let x = 0; - payEvents.forEach(p => { - let date = new Date((p.timestamp ?? 0) * 1000).toUTCString() - - if (date.includes(', ')) date = date.split(', ')[1] - - rows.push([ - date, - p.amount.format(), - // p.amountUSD ? p.amountUSD.format() : 'n/a', TODO: not working for V4 (check subgraph) - p.beneficiary, - p.txHash, - ]) - }) - - downloadCsvFile(`payments_v4_p${projectId}_block-${blockNumber}`, rows) - } catch (e) { - console.error('Error downloading payouts', e); - emitErrorNotification(t`Error downloading payouts, try again.`); - } finally { - setIsLoading(false); - } - }, [blockNumber, projectId, payEventsData]); - - return { downloadPayments, isLoading }; -}; diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/models/ActivityEvents.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/models/ActivityEvents.ts deleted file mode 100644 index f960855f98..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/models/ActivityEvents.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Ether, JBProjectToken } from 'juice-sdk-core' -import { Address } from 'viem' - -export type PayEvent = { - id: string - amount: Ether - amountUSD: Ether | undefined - beneficiary: Address - beneficiaryTokenCount?: JBProjectToken - timestamp: number - txHash: string -} - -export type ActivityEvents = { - payEvents: PayEvent[] - // TODO: other event types -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/utils/transformEventsData.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/utils/transformEventsData.ts deleted file mode 100644 index 243a00239d..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ActivityPanel/utils/transformEventsData.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Ether, JBProjectToken } from 'juice-sdk-core' -import { PayEventsQuery } from 'packages/v4/graphql/client/graphql' -import { PayEvent } from '../models/ActivityEvents' - -export function transformPayEventsRes( - data: PayEventsQuery | undefined, -): PayEvent[] | undefined { - return data?.payEvents.map(event => { - return { - id: event.id, - amount: new Ether(BigInt(event.amount)), - amountUSD: event.amountUSD ? new Ether(BigInt(event.amountUSD)) : undefined, - beneficiary: event.beneficiary, - beneficiaryTokenCount: new JBProjectToken( - BigInt(event.beneficiaryTokenCount), - ), - timestamp: event.timestamp, - txHash: event.txHash, - } - }) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx deleted file mode 100644 index eef3a5898c..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4DistributePayoutsModal.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { t, Trans } from '@lingui/macro' -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 { 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 { fromWad } from 'utils/format/formatNumber' -import { useV4DistributableAmount } from './hooks/useV4DistributableAmount' - -export default function V4DistributePayoutsModal({ - open, - onCancel, - onConfirmed, -}: { - open?: boolean - onCancel?: VoidFunction - onConfirmed?: VoidFunction -}) { - const { splits: payoutSplits } = useV4CurrentPayoutSplits() - const { data: usedPayoutLimit } = useUsedPayoutLimitOf() - const { data: payoutLimit } = usePayoutLimit() - const { data: balanceOfNativeTerminal } = useV4BalanceOfNativeTerminal() - const { distributableAmount: distributable } = useV4DistributableAmount() - - const payoutLimitAmount = payoutLimit?.amount - const payoutLimitAmountCurrency = payoutLimit?.currency - - const [transactionPending, setTransactionPending] = useState() - const [loading, setLoading] = useState() - const [distributionAmount, setDistributionAmount] = useState() - - // TODO: const v4DistributePayoutsTx = useV4DistributePayoutsTx() - - useEffect(() => { - if (!payoutLimitAmount) return - - const unusedFunds = payoutLimitAmount - (usedPayoutLimit ?? 0n) ?? 0n - const distributable = balanceOfNativeTerminal && balanceOfNativeTerminal > unusedFunds - ? unusedFunds - : 0n - - setDistributionAmount(fromWad(distributable)) - }, [ - balanceOfNativeTerminal, - payoutLimitAmount, - usedPayoutLimit, - ]) - - async function executeDistributePayoutsTx() { - if (!payoutLimitAmountCurrency || !distributionAmount) return - - setLoading(true) - - const txSuccessful = true - // TODO: const txSuccessful = await v4DistributePayoutsTx( - // { - // amount: distributionAmount, - // currency: payoutLimitAmountCurrency, - // }, - // { - // onDone: () => { - // setTransactionPending(true) - // }, - // onConfirmed: () => { - // setLoading(false) - // setTransactionPending(false) - // onConfirmed?.() - // }, - // }, - // ) - - if (!txSuccessful) { - setLoading(false) - setTransactionPending(false) - } - } - - const currencyName = V4CurrencyName(payoutLimitAmountCurrency) - - return ( - Send payouts} - open={open} - onOk={executeDistributePayoutsTx} - onCancel={() => { - setDistributionAmount(undefined) - onCancel?.() - }} - okButtonProps={{ - disabled: !distributionAmount || distributionAmount === '0', - }} - confirmLoading={loading} - transactionPending={transactionPending} - okText={t`Send payouts`} - connectWalletText={t`Connect wallet to send payouts`} - width={640} - className="top-[40px]" - > -
-
- Amount to pay out} - extra={Recipients will recieve payouts in ETH} - > - setDistributionAmount(value)} - min={0} - accessory={ -
- - {currencyName} - - MAX} - onClick={() => - setDistributionAmount(distributable.format()) - } - /> -
- } - /> -
-
-
-
- Payout recipients -
- -
- -
-
- - {FEES_EXPLANATION} -
-
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ExportPayoutsCsvItem.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ExportPayoutsCsvItem.tsx deleted file mode 100644 index 6b7d46cc6f..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ExportPayoutsCsvItem.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { ArrowUpTrayIcon } from '@heroicons/react/24/outline' -import { Trans, t } from '@lingui/macro' -import { Button, Tooltip } from 'antd' -import { useJBRuleset } from 'juice-sdk-react' -import { twMerge } from 'tailwind-merge' -import { useV4CurrentUpcomingPayoutSplits } from './hooks/useV4CurrentUpcomingPayoutSplits' -import { useV4ExportSplitsToCsv } from './hooks/useV4ExportSplitsToCsv' - -export const V4ExportPayoutsCsvItem = ({ - type, -}: { - type: 'current' | 'upcoming' -}) => { - const { splits: payoutSplits, isLoading } = useV4CurrentUpcomingPayoutSplits(type) - const { data: ruleset } = useJBRuleset() - const fcNumber = ruleset - ? type === 'current' - ? Number(ruleset.cycleNumber) - : Number(ruleset.cycleNumber) + 1 - : undefined - const disabled = !payoutSplits?.length - const { exportSplitsToCsv } = useV4ExportSplitsToCsv( - payoutSplits ?? [], - 'payouts', - fcNumber, - ) - - return ( - - - - ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4PayoutsSubPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4PayoutsSubPanel.tsx deleted file mode 100644 index e1e3331f43..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4PayoutsSubPanel.tsx +++ /dev/null @@ -1,93 +0,0 @@ -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' -import { V4ProjectAllocationRow } from './V4ProjectAllocationRow' -import { V4SendPayoutsButton } from './V4SendPayoutsButton' -import { V4TreasuryStats } from './V4TreasuryStats' - -export const V4PayoutsSubPanel = ({ - className, - type, -}: { - className?: string - type: 'current' | 'upcoming' -}) => { - const { payouts, isLoading, totalPayoutAmount, payoutLimit } = - useV4PayoutsSubPanel(type) - - const hasPayouts = useMemo(() => { - if (!payouts || payouts.length === 0 || payoutLimit === 0n) - return false - return true - }, [payouts, payoutLimit]) - return ( -
-

- Treasury & Payouts -

-
- {type === 'current' && } - - , - }, - ], - }, - } - : {})} - > -
- {isLoading ? ( - Array.from({ length: 5 }).map((_, i) => ( - - )) - ) : hasPayouts ? ( - payouts?.map((payout, i) => ( - - )) - ) : ( - - None - - )} -
- {hasPayouts && type === 'current' && ( - - )} -
-
-
- ) -} - -const ProjectAllocationSkeleton = () => ( -
-
- - - - -
-
- -
-
-) diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ProjectAllocationRow.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ProjectAllocationRow.tsx deleted file mode 100644 index 6ab33ff5b1..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4ProjectAllocationRow.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { JuiceboxAccountLink } from 'components/JuiceboxAccountLink' -import { useRouter } from 'next/router' -import V4ProjectHandleLink from 'packages/v4/components/V4ProjectHandleLink' -import { ReactNode } from 'react' - -type V4ProjectAllocationRowProps = { - projectId: number | undefined - address: string - amount?: ReactNode | string - percent: string -} - -export const V4ProjectAllocationRow: React.FC = ({ - address, - projectId, - amount, - percent, -}) => { - const router = useRouter() - const { chainName } = router.query - return ( -
-
- - {projectId ? ( - <> - - - ) : ( - - )} - -
-
- {amount ? ( - <> - {amount} - ({percent}) - - ) : ( - {percent} - )} -
-
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4SendPayoutsButton.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4SendPayoutsButton.tsx deleted file mode 100644 index e65c98916d..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4SendPayoutsButton.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { ArrowUpCircleIcon } from '@heroicons/react/24/outline' -import { Trans } from '@lingui/macro' -import { Button, Tooltip } from 'antd' -import { useCallback, useMemo, useState } from 'react' -import { twMerge } from 'tailwind-merge' -import { useV4DistributableAmount } from './hooks/useV4DistributableAmount' -import V4DistributePayoutsModal from './V4DistributePayoutsModal' - -export const V4SendPayoutsButton = ({ - className, - containerClassName, -}: { - className?: string - containerClassName?: string -}) => { - const { distributableAmount } = useV4DistributableAmount() - const [open, setOpen] = useState(false) - const openModal = useCallback(() => setOpen(true), []) - const closeModal = useCallback(() => setOpen(false), []) - - const distributeButtonDisabled = useMemo(() => { - return distributableAmount.value === 0n - }, [distributableAmount]) - - return ( - No payouts remaining for this cycle.} - open={distributeButtonDisabled ? undefined : false} - className={containerClassName} - > - - - - ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4TreasuryStats.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4TreasuryStats.tsx deleted file mode 100644 index fcfc62e61f..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/V4TreasuryStats.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Trans, t } from '@lingui/macro' -import { - availableToPayOutTooltip, - treasuryBalanceTooltip, -} from 'components/Project/ProjectTabs/CyclesPayoutsTab/CyclesPanelTooltips' -import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard' -import { useMemo } from 'react' -import { useV4TreasuryStats } from './hooks/useV4TreasuryStats' - -export const V4TreasuryStats = () => { - const { availableToPayout, surplus, treasuryBalance, redemptionRate } = - useV4TreasuryStats() - - const surplusTooltip = useMemo( - () => - redemptionRate && redemptionRate.toFloat() > 0 ? ( - - {surplus} is available for token redemptions or future payouts. - - ) : ( - {surplus} is available for future payouts. - ), - [surplus, redemptionRate], - ) - - return ( -
- - - -
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingConfigurationPanel.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingConfigurationPanel.ts deleted file mode 100644 index 57c34443c8..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingConfigurationPanel.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useV4CycleSection } from './useV4CycleSection' -import { useV4ExtensionSection } from './useV4ExtensionSection' -import { useV4OtherRulesSection } from './useV4OtherRulesSection' -import { useV4TokenSection } from './useV4TokenSection' - - -export const useV4CurrentUpcomingConfigurationPanel = ( - type: 'current' | 'upcoming', -) => { - const cycle = useV4CycleSection(type) - const token = useV4TokenSection(type) - const otherRules = useV4OtherRulesSection(type) - const extension = useV4ExtensionSection(type) - - return { - cycle, - token, - otherRules, - extension, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingPayoutLimit.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingPayoutLimit.ts deleted file mode 100644 index 1172372901..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingPayoutLimit.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { usePayoutLimit } from 'packages/v4/hooks/usePayoutLimit' -import { useUpcomingPayoutLimit } from 'packages/v4/hooks/useUpcomingPayoutLimit' - -export const useV4CurrentUpcomingPayoutLimit = ( - type: 'current' | 'upcoming', -) => { - const { data: payoutLimit, isLoading: currentLoading } = usePayoutLimit() - - const { data: upcomingPayoutLimit, isLoading: upcomingLoading } = useUpcomingPayoutLimit() - - if (type === 'current') { - return { - payoutLimit: payoutLimit?.amount, - payoutLimitCurrency: payoutLimit?.currency, - loading: currentLoading, - } - } - return { - distributionLimit: upcomingPayoutLimit.amount, - distributionLimitCurrency: upcomingPayoutLimit.currency, - loading: upcomingLoading - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingPayoutSplits.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingPayoutSplits.ts deleted file mode 100644 index 0813a0d511..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CurrentUpcomingPayoutSplits.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { JBSplit, SplitPortion } from 'juice-sdk-core' -import { - useJBContractContext, - useReadJbSplitsSplitsOf, - useReadJbTokensTokenOf, -} from 'juice-sdk-react' -import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' -import { useV4CurrentPayoutSplits } from 'packages/v4/hooks/useV4PayoutSplits' - -export const useV4CurrentUpcomingPayoutSplits = ( - type: 'current' | 'upcoming', -) => { - const { projectId } = useJBContractContext() - const { data: tokenAddress } = useReadJbTokensTokenOf() - const { splits, isLoading: currentSplitsLoading } = useV4CurrentPayoutSplits() - - const { ruleset: upcomingRuleset, isLoading: upcomingRulesetLoading } = - useJBUpcomingRuleset() - - const { data: _upcomingSplits, isLoading: upcomingSplitsLoading } = - useReadJbSplitsSplitsOf({ - args: [ - projectId, - BigInt(upcomingRuleset?.id ?? 0), - BigInt(tokenAddress ?? 0), - ], - query: { - select(data) { - return data.map(d => ({ - ...d, - percent: new SplitPortion(d.percent), - })) - }, - }, - }) - - const upcomingSplits: JBSplit[] = _upcomingSplits ? [..._upcomingSplits] : [] - - if (type === 'current') { - return { splits, isLoading: currentSplitsLoading } - } - return { - splits: upcomingSplits, - isLoading: upcomingSplitsLoading || upcomingRulesetLoading, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CycleSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CycleSection.ts deleted file mode 100644 index e8dcc0f9b7..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4CycleSection.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { ConfigurationPanelTableData } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { useJBRuleset } from 'juice-sdk-react' -import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' -import { usePayoutLimit } from 'packages/v4/hooks/usePayoutLimit' -import { useUpcomingPayoutLimit } from 'packages/v4/hooks/useUpcomingPayoutLimit' -import { useV4FormatConfigurationCycleSection } from './useV4FormatConfigurationCycleSection' - -export const useV4CycleSection = ( - type: 'current' | 'upcoming', -): ConfigurationPanelTableData => { - const { data: ruleset } = useJBRuleset() - - const { ruleset: upcomingRuleset, isLoading: upcomingRulesetLoading } = useJBUpcomingRuleset() - - const { data: payoutLimits } = usePayoutLimit() - const payoutLimitAmount = payoutLimits?.amount - const payoutLimitCurrency = payoutLimits?.currency - - const { data: upcomingPayoutLimit, isLoading: upcomingPayoutLimitLoading } = useUpcomingPayoutLimit() - const upcomingPayoutLimitAmount = upcomingPayoutLimit?.amount - const upcomingPayoutLimitCurrency = upcomingPayoutLimit?.currency - - return useV4FormatConfigurationCycleSection({ - ruleset, - payoutLimitAmountCurrency: { - amount: payoutLimitAmount, - currency: payoutLimitCurrency, - }, - upcomingRulesetLoading, - upcomingPayoutLimitLoading, - upcomingRuleset, - upcomingPayoutLimitAmountCurrency: { - amount: upcomingPayoutLimitAmount, - currency: upcomingPayoutLimitCurrency, - }, - - // Hide upcoming info from current section. - ...(type === 'current' && { - upcomingRuleset: null, - upcomingPayoutLimitAmountCurrency: null, - }), - }) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4DistributableAmount.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4DistributableAmount.ts deleted file mode 100644 index 5929caacc7..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4DistributableAmount.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { JBProjectToken } from 'juice-sdk-core' -import { usePayoutLimit } from 'packages/v4/hooks/usePayoutLimit' -import { useUsedPayoutLimitOf } from 'packages/v4/hooks/useUsedPayoutLimitOf' -import { useV4BalanceOfNativeTerminal } from 'packages/v4/hooks/useV4BalanceOfNativeTerminal' -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 distributable = - effectiveDistributionLimit === 0n - ? effectiveDistributionLimit - : effectiveDistributionLimit - distributedAmount - - const distributableAmount = treasuryBalance > distributable - ? distributable - : treasuryBalance - - return { - distributableAmount: new JBProjectToken(distributableAmount), - currency: payoutLimit?.currency, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4ExportSplitsToCsv.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4ExportSplitsToCsv.ts deleted file mode 100644 index 75a75392ad..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4ExportSplitsToCsv.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { t } from '@lingui/macro' -import { JBSplit } from 'juice-sdk-core' -import { useJBContractContext } from 'juice-sdk-react' -import useProjectOwnerOf from 'packages/v4/hooks/useV4ProjectOwnerOf' -import { v4GetProjectOwnerRemainderSplit } from 'packages/v4/utils/v4Splits' -import { useState } from 'react' -import { downloadCsvFile } from 'utils/csv' -import { emitErrorNotification } from 'utils/notifications' -import { Hash } from 'viem' - -const CSV_HEADER = [ - 'beneficiary', - 'percent', - 'preferAddToBalance', - 'lockedUntil', - 'projectId', - 'hook', -] - -const splitToCsvRow = (split: JBSplit) => { - return [ - split.beneficiary, - `${split.percent.formatPercentage()}%`, - `${split.preferAddToBalance}`, - `${split.lockedUntil}`, - split.projectId.toString(), - split.hook, - ] -} - -const prepareSplitsCsv = ( - splits: JBSplit[], - projectOwnerAddress: Hash, -): (string | undefined)[][] => { - const csvContent = splits.map(splitToCsvRow) - - const rows = [CSV_HEADER, ...csvContent] - - const projectOwnerSplit = v4GetProjectOwnerRemainderSplit( - projectOwnerAddress, - splits, - ) - if (projectOwnerSplit.percent.toFloat() > 0) { - rows.push(splitToCsvRow(projectOwnerSplit)) - } - - return rows -} - -export const useV4ExportSplitsToCsv = ( - splits: JBSplit[], - splitName = 'splits', - fcNumber?: number, -) => { - const { data: projectOwnerAddress } = useProjectOwnerOf() - const { projectId } = useJBContractContext() - const [loading, setLoading] = useState(false) - - const handle = undefined - - const exportSplitsToCsv = () => { - if (!splits || !projectOwnerAddress) { - emitErrorNotification( - t`CSV data wasn't ready for export. Wait a few seconds and try again.`, - ) - return - } - - setLoading(true) - - try { - const csvContent = prepareSplitsCsv(splits, projectOwnerAddress) - const projectIdentifier = handle ? `@${handle}` : `project-${projectId}` - const filename = `${projectIdentifier}_${splitName}${ - fcNumber ? `_fc-${fcNumber}` : '' - }` - - downloadCsvFile(filename, csvContent) - } catch (e) { - console.error(e) - emitErrorNotification(t`CSV download failed.`) - } finally { - setLoading(false) - } - } - - return { exportSplitsToCsv, loading } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4ExtensionSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4ExtensionSection.ts deleted file mode 100644 index 513d301ef7..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4ExtensionSection.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ConfigurationPanelTableData } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { useJBRulesetMetadata } from 'juice-sdk-react' -import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' -import { useV4FormatConfigurationExtensionSection } from './useV4FormatConfigurationExtensionSection' - -export const useV4ExtensionSection = ( - type: 'current' | 'upcoming', -): ConfigurationPanelTableData | null => { - const { data: rulesetMetadata } = useJBRulesetMetadata() - const { - rulesetMetadata: upcomingRulesetMetadata, - } = useJBUpcomingRuleset() - - - return useV4FormatConfigurationExtensionSection({ - rulesetMetadata, - upcomingRulesetMetadata, - ...(type === 'current' && { - upcomingRulesetMetadata: null, - }), - }) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationCycleSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationCycleSection.ts deleted file mode 100644 index 23e017d81d..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationCycleSection.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { t } from '@lingui/macro' -import { ConfigurationPanelDatum } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' -import { JBRulesetData } from 'juice-sdk-core' -import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption' -import { getApprovalStrategyByAddress } from 'packages/v4/utils/approvalHooks' -import { formatCurrencyAmount } from 'packages/v4/utils/formatCurrencyAmount' -import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math' -import { useMemo } from 'react' -import { formatTime } from 'utils/format/formatTime' -import { timeSecondsToDateString } from 'utils/timeSecondsToDateString' - -export const useV4FormatConfigurationCycleSection = ({ - ruleset, - payoutLimitAmountCurrency, - upcomingRuleset, - upcomingRulesetLoading, - upcomingPayoutLimitLoading, - upcomingPayoutLimitAmountCurrency, -}: { - ruleset?: JBRulesetData | null - payoutLimitAmountCurrency: { - amount: bigint | undefined - currency: V4CurrencyOption | undefined - } - upcomingRuleset?: JBRulesetData | null - upcomingRulesetLoading: boolean - upcomingPayoutLimitLoading: boolean - upcomingPayoutLimitAmountCurrency?: { - amount: bigint | undefined - currency: V4CurrencyOption | undefined - } | null -}) => { - const formatDuration = (duration: number | undefined) => { - if (duration === undefined) return undefined - if (duration === 0) return t`Not set` - return timeSecondsToDateString(Number(duration), 'short', 'lower') - } - - const durationDatum: ConfigurationPanelDatum = useMemo(() => { - const currentDuration = formatDuration(ruleset?.duration) - if (upcomingRuleset === null || upcomingRulesetLoading) { - return pairToDatum(t`Duration`, currentDuration, null) - } - const upcomingDuration = formatDuration( - upcomingRuleset ? upcomingRuleset?.duration : ruleset?.duration, - ) - - return pairToDatum(t`Duration`, currentDuration, upcomingDuration) - }, [ruleset?.duration, upcomingRuleset, upcomingRulesetLoading]) - - const upcomingRulesetStart = ruleset?.start - ? ruleset.start + (ruleset?.duration || 0) - : 0 - - const startTimeDatum: ConfigurationPanelDatum = useMemo(() => { - const formattedTime = - upcomingRuleset === null - ? formatTime(ruleset?.start) - : ruleset?.duration === 0 - ? t`Any time` - : formatTime(upcomingRulesetStart) - - const formatTimeDatum: ConfigurationPanelDatum = { - name: t`Start time`, - new: formattedTime, - easyCopy: true, - } - return formatTimeDatum - }, [ruleset?.start, ruleset?.duration, upcomingRuleset, upcomingRulesetStart]) - - const formatPayoutAmount = ( - amount: bigint | undefined, - currency: V4CurrencyOption | undefined, - ) => { - if (amount === undefined || amount === MAX_PAYOUT_LIMIT) return t`Unlimited` - if (amount === 0n) return t`Zero (no payouts)` - return formatCurrencyAmount({ - amount: Number(amount) / 1e18, // Assuming fromWad - currency, - }) - } - - const payoutsDatum: ConfigurationPanelDatum = useMemo(() => { - const { amount, currency } = payoutLimitAmountCurrency ?? {} - const currentPayout = formatPayoutAmount(amount, currency) - - if ( - upcomingPayoutLimitAmountCurrency === null || - upcomingPayoutLimitLoading - ) { - return pairToDatum(t`Payouts`, currentPayout, null) - } - - const upcomingPayoutLimit = - upcomingPayoutLimitAmountCurrency?.amount !== undefined - ? upcomingPayoutLimitAmountCurrency.amount - : amount - const upcomingPayoutLimitCurrency = - upcomingPayoutLimitAmountCurrency?.currency !== undefined - ? upcomingPayoutLimitAmountCurrency.currency - : currency - const upcomingPayout = formatPayoutAmount( - upcomingPayoutLimit, - upcomingPayoutLimitCurrency, - ) - - return pairToDatum(t`Payouts`, currentPayout, upcomingPayout) - }, [ - payoutLimitAmountCurrency, - upcomingPayoutLimitAmountCurrency, - upcomingPayoutLimitLoading, - ]) - - const editDeadlineDatum: ConfigurationPanelDatum = useMemo(() => { - const currentApprovalStrategy = ruleset?.approvalHook - ? getApprovalStrategyByAddress(ruleset.approvalHook) - : undefined - const current = currentApprovalStrategy?.name - if (upcomingRuleset === null || upcomingPayoutLimitLoading) { - return pairToDatum(t`Edit deadline`, current, null) - } - - const upcomingBallotStrategy = upcomingRuleset?.approvalHook - ? getApprovalStrategyByAddress(upcomingRuleset.approvalHook) - : ruleset?.approvalHook - ? getApprovalStrategyByAddress(ruleset.approvalHook) - : undefined - - const upcoming = upcomingBallotStrategy?.name - return pairToDatum(t`Edit deadline`, current, upcoming) - }, [ruleset?.approvalHook, upcomingRuleset, upcomingPayoutLimitLoading]) - - return useMemo(() => { - return { - duration: durationDatum, - startTime: startTimeDatum, - payouts: payoutsDatum, - editDeadline: editDeadlineDatum, - } - }, [durationDatum, startTimeDatum, editDeadlineDatum, payoutsDatum]) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationExtensionSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationExtensionSection.ts deleted file mode 100644 index 80805fc0a9..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationExtensionSection.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { t } from '@lingui/macro' -import { - ConfigurationPanelDatum, - ConfigurationPanelTableData, -} from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' -import { JBRulesetMetadata } from 'juice-sdk-core' -import { useMemo } from 'react' -import { isZeroAddress } from 'utils/address' -import { etherscanLink } from 'utils/etherscan' - -export const useV4FormatConfigurationExtensionSection = ({ - rulesetMetadata, - upcomingRulesetMetadata, -}: { - rulesetMetadata: JBRulesetMetadata | null | undefined - upcomingRulesetMetadata: JBRulesetMetadata | null | undefined -}): ConfigurationPanelTableData | null => { - const contractDatum: ConfigurationPanelDatum = useMemo(() => { - const currentContract = rulesetMetadata?.dataHook - const upcomingContract = upcomingRulesetMetadata?.dataHook - - if (upcomingRulesetMetadata === null) { - const link = currentContract - ? etherscanLink('address', currentContract) - : undefined - return pairToDatum(t`Contract`, currentContract, null, link, true) - } - - const link = upcomingContract - ? etherscanLink('address', upcomingContract) - : currentContract - ? etherscanLink('address', currentContract) - : undefined - return pairToDatum( - t`Contract`, - currentContract, - upcomingContract, - link, - true, - ) - }, [rulesetMetadata?.dataHook, upcomingRulesetMetadata]) - - const useForPaymentsDatum: ConfigurationPanelDatum = useMemo(() => { - const currentUseForPayments = rulesetMetadata?.useDataHookForPay - const upcomingUseForPayments = - upcomingRulesetMetadata?.useDataHookForPay - - if (upcomingRulesetMetadata === null) { - return flagPairToDatum(t`Use for payments`, currentUseForPayments, null) - } - - return flagPairToDatum( - t`Use for payments`, - currentUseForPayments, - upcomingUseForPayments, - ) - }, [rulesetMetadata?.useDataHookForPay, upcomingRulesetMetadata]) - - const useForRedemptionsDatum: ConfigurationPanelDatum = useMemo(() => { - const currentUseForRedemptions = - rulesetMetadata?.useDataHookForRedeem - const upcomingUseForRedemptions = - upcomingRulesetMetadata?.useDataHookForRedeem - - if (upcomingRulesetMetadata === null) { - return flagPairToDatum( - t`Use for redemptions`, - currentUseForRedemptions, - null, - ) - } - - return flagPairToDatum( - t`Use for redemptions`, - currentUseForRedemptions, - upcomingUseForRedemptions, - ) - }, [ - rulesetMetadata?.useDataHookForRedeem, - upcomingRulesetMetadata, - ]) - - const formatted = useMemo(() => { - return { - contract: contractDatum, - useForPayments: useForPaymentsDatum, - useForRedemptions: useForRedemptionsDatum, - } - }, [contractDatum, useForPaymentsDatum, useForRedemptionsDatum]) - - if ( - isZeroAddress(rulesetMetadata?.dataHook) && - !upcomingRulesetMetadata - ) { - return null - } - - return formatted -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationOtherRulesSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationOtherRulesSection.ts deleted file mode 100644 index fa88d1aeff..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationOtherRulesSection.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { t } from '@lingui/macro' -import { - ConfigurationPanelDatum, - ConfigurationPanelTableData, -} from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' -import { JBRulesetMetadata } from 'juice-sdk-core' -import { useMemo } from 'react' - -export const useV4FormatConfigurationOtherRulesSection = ({ - rulesetMetadata, - upcomingRulesetMetadata, -}: { - rulesetMetadata: JBRulesetMetadata | undefined | null - upcomingRulesetMetadata: JBRulesetMetadata | undefined | null -}): ConfigurationPanelTableData => { - const paymentsToThisProjectDatum: ConfigurationPanelDatum = useMemo(() => { - const currentPaymentsToThisProject = - rulesetMetadata?.pausePay !== undefined - ? !rulesetMetadata.pausePay - : undefined - - if (upcomingRulesetMetadata === null) { - return flagPairToDatum( - t`Payments to this project`, - currentPaymentsToThisProject, - null, - ) - } - - const upcomingPaymentsToThisProject = - upcomingRulesetMetadata?.pausePay !== undefined - ? !upcomingRulesetMetadata.pausePay - : undefined - - return flagPairToDatum( - t`Payments to this project`, - currentPaymentsToThisProject, - upcomingPaymentsToThisProject, - ) - }, [rulesetMetadata?.pausePay, upcomingRulesetMetadata]) - - const holdFeesDatum = useMemo(() => { - const currentHoldFees = rulesetMetadata?.holdFees - if (upcomingRulesetMetadata === null) { - return flagPairToDatum(t`Hold fees`, currentHoldFees, null) - } - const upcomingHoldFees = upcomingRulesetMetadata?.holdFees - - return flagPairToDatum(t`Hold fees`, currentHoldFees, upcomingHoldFees) - }, [rulesetMetadata?.holdFees, upcomingRulesetMetadata]) - - const setPaymentTerminalsDatum: ConfigurationPanelDatum = useMemo(() => { - const currentSetPaymentTerminals = - rulesetMetadata?.allowSetTerminals - if (upcomingRulesetMetadata === null) { - return flagPairToDatum( - t`Set payment terminals`, - currentSetPaymentTerminals, - null, - ) - } - const upcomingSetPaymentTerminals = - upcomingRulesetMetadata?.allowSetTerminals - - return flagPairToDatum( - t`Set payment terminals`, - currentSetPaymentTerminals, - upcomingSetPaymentTerminals, - ) - }, [ - rulesetMetadata?.allowSetTerminals, - upcomingRulesetMetadata, - ]) - - const setControllerDatum: ConfigurationPanelDatum = useMemo(() => { - const currentSetController = rulesetMetadata?.allowSetController - if (upcomingRulesetMetadata === null) { - return flagPairToDatum(t`Set controller`, currentSetController, null) - } - const upcomingSetController = - upcomingRulesetMetadata?.allowSetController - - return flagPairToDatum( - t`Set controller`, - currentSetController, - upcomingSetController, - ) - }, [ - rulesetMetadata?.allowSetController, - upcomingRulesetMetadata, - ]) - - // Generate the rest of the data, copilot - // Migrate payment terminal - // Migrate controller - const migratePaymentTerminalDatum: ConfigurationPanelDatum = useMemo(() => { - const currentMigratePaymentTerminal = - rulesetMetadata?.allowTerminalMigration - if (upcomingRulesetMetadata === null) { - return flagPairToDatum( - t`Migrate payment terminal`, - currentMigratePaymentTerminal, - null, - ) - } - const upcomingMigratePaymentTerminal = - upcomingRulesetMetadata?.allowTerminalMigration - - return flagPairToDatum( - t`Migrate payment terminal`, - currentMigratePaymentTerminal, - upcomingMigratePaymentTerminal, - ) - }, [ - rulesetMetadata?.allowTerminalMigration, - upcomingRulesetMetadata, - ]) - - return useMemo(() => { - return { - paymentsToThisProject: paymentsToThisProjectDatum, - holdFees: holdFeesDatum, - setPaymentTerminals: setPaymentTerminalsDatum, - setController: setControllerDatum, - migratePaymentTerminal: migratePaymentTerminalDatum, - } - }, [ - holdFeesDatum, - migratePaymentTerminalDatum, - paymentsToThisProjectDatum, - setControllerDatum, - setPaymentTerminalsDatum, - ]) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationTokenSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationTokenSection.ts deleted file mode 100644 index afaf6d49c6..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4FormatConfigurationTokenSection.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { t } from '@lingui/macro' -import { - ConfigurationPanelDatum, - ConfigurationPanelTableData, -} from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { flagPairToDatum } from 'components/Project/ProjectTabs/utils/flagPairToDatum' -import { pairToDatum } from 'components/Project/ProjectTabs/utils/pairToDatum' -import { JBRulesetData, JBRulesetMetadata } from 'juice-sdk-core' -import { useMemo } from 'react' -import { tokenSymbolText } from 'utils/tokenSymbolText' - -export const useV4FormatConfigurationTokenSection = ({ - ruleset, - rulesetMetadata, - tokenSymbol: tokenSymbolRaw, - upcomingRuleset, - upcomingRulesetLoading, - upcomingRulesetMetadata, -}: { - ruleset: JBRulesetData | undefined | null - rulesetMetadata: JBRulesetMetadata | undefined | null - tokenSymbol: string | undefined - upcomingRuleset: JBRulesetData | undefined | null - upcomingRulesetLoading: boolean - upcomingRulesetMetadata?: JBRulesetMetadata | undefined | null -}): ConfigurationPanelTableData => { - const tokenSymbol = useMemo( - () => - tokenSymbolText({ - tokenSymbol: tokenSymbolRaw, - capitalize: false, - plural: true, - }), - [tokenSymbolRaw], - ) - const decayPercentFloat = ruleset?.decayPercent.toFloat() - const currentTotalIssuanceRate = ruleset?.weight.toFloat() - const queuedTotalIssuanceRate = upcomingRuleset ? - upcomingRuleset?.weight.toFloat() - : currentTotalIssuanceRate && decayPercentFloat ? - currentTotalIssuanceRate - (currentTotalIssuanceRate * decayPercentFloat) - : undefined - - const totalIssuanceRateDatum: ConfigurationPanelDatum = useMemo(() => { - const current = currentTotalIssuanceRate - ? `${currentTotalIssuanceRate} ${tokenSymbol}/ETH` - : undefined - - if (upcomingRuleset === null || upcomingRulesetLoading) { - return pairToDatum(t`Total issuance rate`, current, null) - } - const queued = queuedTotalIssuanceRate - ? `${queuedTotalIssuanceRate} ${tokenSymbol}/ETH` - : undefined - return pairToDatum(t`Total issuance rate`, current, queued) - }, [upcomingRuleset, currentTotalIssuanceRate, tokenSymbol, queuedTotalIssuanceRate, upcomingRulesetLoading]) - - const reservedPercentFloat = rulesetMetadata?.reservedPercent.toFloat() - const queuedReservedPercentFloat = upcomingRulesetMetadata?.reservedPercent.toFloat() - - const payerIssuanceRateDatum: ConfigurationPanelDatum = useMemo(() => { - const currentPayerIssuanceRate = currentTotalIssuanceRate && reservedPercentFloat ? - currentTotalIssuanceRate - (currentTotalIssuanceRate * reservedPercentFloat) - : undefined - - const current = currentPayerIssuanceRate - ? `${currentPayerIssuanceRate} ${tokenSymbol}/ETH` - : undefined - if (upcomingRuleset === null || upcomingRulesetMetadata === null || upcomingRulesetLoading) { - return pairToDatum(t`Payer issuance rate`, current, null) - } - const _reservedPercent = queuedReservedPercentFloat ?? reservedPercentFloat - const queuedPayerIssuanceRate = queuedTotalIssuanceRate && _reservedPercent ? - queuedTotalIssuanceRate - (queuedTotalIssuanceRate * _reservedPercent) - : undefined - const queued = queuedPayerIssuanceRate - ? `${queuedPayerIssuanceRate} ${tokenSymbol}/ETH` - : undefined - return pairToDatum(t`Payer issuance rate`, current, queued) - }, [ - tokenSymbol, - upcomingRuleset, - queuedReservedPercentFloat, - upcomingRulesetMetadata, - currentTotalIssuanceRate, - queuedTotalIssuanceRate, - reservedPercentFloat, - upcomingRulesetLoading - ]) - - const reservedPercentDatum: ConfigurationPanelDatum = useMemo(() => { - const current = rulesetMetadata?.reservedPercent ? - `${rulesetMetadata.reservedPercent.formatPercentage()}%` : undefined - if (upcomingRulesetMetadata === null || upcomingRulesetLoading) { - return pairToDatum(t`Reserved rate`, current, null) - } - - const queued = upcomingRulesetMetadata?.reservedPercent - ? `${upcomingRulesetMetadata.reservedPercent.formatPercentage()}%` - : rulesetMetadata?.reservedPercent ? - `${rulesetMetadata.reservedPercent.formatPercentage()}%` - : undefined - return pairToDatum(t`Reserved rate`, current, queued) - }, [upcomingRulesetMetadata, rulesetMetadata, upcomingRulesetLoading]) - - const decayPercentDatum: ConfigurationPanelDatum = useMemo(() => { - const current = ruleset ? - `${ruleset.decayPercent.formatPercentage()}%` - : undefined - - if (upcomingRuleset === null || upcomingRulesetLoading) { - return pairToDatum(t`Decay rate`, current, null) - } - const queued = upcomingRuleset - ? `${upcomingRuleset.decayPercent.formatPercentage()}%` - : ruleset ? - `${ruleset.decayPercent.formatPercentage()}%` - : undefined - - return pairToDatum(t`Decay rate`, current, queued) - }, [ruleset, upcomingRuleset, upcomingRulesetLoading]) - - const redemptionRateDatum: ConfigurationPanelDatum = useMemo(() => { - const currentRedemptionRate = rulesetMetadata?.redemptionRate.formatPercentage() - - const current = currentRedemptionRate - ? `${currentRedemptionRate}%` - : undefined - - if (upcomingRulesetMetadata === null || upcomingRulesetLoading) { - return pairToDatum(t`Redemption rate`, current, null) - } - - const queued = upcomingRulesetMetadata - ? `${upcomingRulesetMetadata?.redemptionRate.formatPercentage()}%` - : rulesetMetadata ? - `${rulesetMetadata.redemptionRate.formatPercentage()}%` - : undefined - return pairToDatum(t`Redemption rate`, current, queued) - }, [upcomingRulesetMetadata, rulesetMetadata, upcomingRulesetLoading]) - - const ownerTokenMintingRateDatum: ConfigurationPanelDatum = useMemo(() => { - const currentOwnerTokenMintingRate = - rulesetMetadata?.allowOwnerMinting !== undefined - ? rulesetMetadata?.allowOwnerMinting - : undefined - if (upcomingRulesetMetadata === null || upcomingRulesetLoading) { - return flagPairToDatum( - t`Owner token minting`, - currentOwnerTokenMintingRate, - null, - ) - } - - const queuedOwnerTokenMintingRate = - upcomingRulesetMetadata?.allowOwnerMinting !== undefined ? - upcomingRulesetMetadata?.allowOwnerMinting - : rulesetMetadata?.allowOwnerMinting !== undefined ? - rulesetMetadata.allowOwnerMinting - : undefined - - return flagPairToDatum( - t`Owner token minting`, - currentOwnerTokenMintingRate, - queuedOwnerTokenMintingRate, - ) - }, [rulesetMetadata?.allowOwnerMinting, upcomingRulesetMetadata, upcomingRulesetLoading]) - - const tokenTransfersDatum: ConfigurationPanelDatum = useMemo(() => { - const currentTokenTransfersDatum = - rulesetMetadata?.pauseCreditTransfers !== undefined - ? !rulesetMetadata?.pauseCreditTransfers - : undefined - if (upcomingRulesetMetadata === null || upcomingRulesetLoading) { - return flagPairToDatum( - t`Token transfers`, - !!currentTokenTransfersDatum, - null, - ) - } - const queuedTokenTransfersDatum = - upcomingRulesetMetadata?.pauseCreditTransfers !== undefined - ? !upcomingRulesetMetadata?.pauseCreditTransfers - : rulesetMetadata?.pauseCreditTransfers !== undefined ? - !rulesetMetadata.pauseCreditTransfers - : null - - return flagPairToDatum( - t`Token transfers`, - currentTokenTransfersDatum, - queuedTokenTransfersDatum, - ) - }, [ - rulesetMetadata?.pauseCreditTransfers, - upcomingRulesetMetadata, - upcomingRulesetLoading - ]) - - return useMemo(() => { - return { - totalIssuanceRate: totalIssuanceRateDatum, - payerIssuanceRate: payerIssuanceRateDatum, - reservedPercent: reservedPercentDatum, - decayPercentDatum: decayPercentDatum, - redemptionRate: redemptionRateDatum, - ownerTokenMintingRate: ownerTokenMintingRateDatum, - tokenTransfers: tokenTransfersDatum, - } - }, [ - decayPercentDatum, - totalIssuanceRateDatum, - ownerTokenMintingRateDatum, - payerIssuanceRateDatum, - redemptionRateDatum, - reservedPercentDatum, - tokenTransfersDatum, - ]) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4OtherRulesSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4OtherRulesSection.ts deleted file mode 100644 index 9e66568513..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4OtherRulesSection.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ConfigurationPanelTableData } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { useJBRulesetMetadata } from 'juice-sdk-react' -import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' -import { useV4FormatConfigurationOtherRulesSection } from './useV4FormatConfigurationOtherRulesSection' - -export const useV4OtherRulesSection = ( - type: 'current' | 'upcoming', -): ConfigurationPanelTableData => { - const { data: rulesetMetadata } = useJBRulesetMetadata() - const { - rulesetMetadata: upcomingRulesetMetadata, - } = useJBUpcomingRuleset() - - return useV4FormatConfigurationOtherRulesSection({ - rulesetMetadata, - upcomingRulesetMetadata, - ...(type === 'current' && { - upcomingRulesetMetadata: null, - }), - }) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4PayoutsSubPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4PayoutsSubPanel.tsx deleted file mode 100644 index dced7b6d74..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4PayoutsSubPanel.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { JBSplit, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' -import { NativeTokenValue, useReadJbMultiTerminalFee } from 'juice-sdk-react' -import useProjectOwnerOf from 'packages/v4/hooks/useV4ProjectOwnerOf' -import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math' -import { v4GetProjectOwnerRemainderSplit } from 'packages/v4/utils/v4Splits' -import { useCallback, useMemo } from 'react' -import assert from 'utils/assert' -import { feeForAmount } from 'utils/math' -import { useV4CurrentUpcomingPayoutLimit } from './useV4CurrentUpcomingPayoutLimit' -import { useV4CurrentUpcomingPayoutSplits } from './useV4CurrentUpcomingPayoutSplits' -import { useV4DistributableAmount } from './useV4DistributableAmount' - -const splitHasFee = (split: JBSplit) => { - return split.projectId || split.projectId > 0n -} - -const calculateSplitAmountWad = ( - split: JBSplit, - payoutLimit: bigint | undefined, - primaryETHTerminalFee: bigint | undefined, -) => { - const splitValue = payoutLimit - ? (payoutLimit * split.percent.value) / BigInt(SPLITS_TOTAL_PERCENT) - : undefined - const feeAmount = splitHasFee(split) - ? feeForAmount(splitValue, primaryETHTerminalFee) ?? 0n - : 0n - return splitValue ? splitValue - feeAmount : undefined -} - -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 transformSplit = useCallback( - (split: JBSplit) => { - assert(split.beneficiary, 'Beneficiary must be defined') - let amount = undefined - const splitAmountWad = calculateSplitAmountWad( - split, - payoutLimit, - primaryNativeTerminalFee, - ) - if (showAmountOnPayout && splitAmountWad && payoutLimitCurrency) { - amount = - } - return { - projectId: split.projectId ? Number(split.projectId) : undefined, - address: split.beneficiary!, - percent: `${split.percent.formatPercentage()}%`, - amount, - } - }, - [ - payoutLimit, - payoutLimitCurrency, - showAmountOnPayout, - primaryNativeTerminalFee, - ], - ) - - const totalPayoutAmount = useMemo(() => { - if (!payoutLimit || !payoutLimitCurrency) return - if (payoutLimit === MAX_PAYOUT_LIMIT || payoutLimit === 0n) return - - return - }, [payoutLimit, payoutLimitCurrency]) - - const payouts = useMemo(() => { - if (isLoading || !splits) return - - if ( - // We don't need to worry about upcoming as this is informational only - type === 'current' && - splits.length === 0 && - payoutLimit === 0n && - distributableAmount.value === 0n - ) { - return [] - } - - const ownerPayout = projectOwnerAddress - ? v4GetProjectOwnerRemainderSplit(projectOwnerAddress, splits) - : undefined - - return [ - ...splits, - ...(ownerPayout && ownerPayout.percent.value > 0n ? [ownerPayout] : []), - ] - .sort((a, b) => Number(b.percent) - Number(a.percent)) - .map(transformSplit) - }, [ - distributableAmount, - projectOwnerAddress, - splits, - isLoading, - payoutLimit, - transformSplit, - type, - ]) - - return { - isLoading, - payouts, - totalPayoutAmount, - payoutLimit, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4TokenSection.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4TokenSection.ts deleted file mode 100644 index baafc6cd8f..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4TokenSection.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ConfigurationPanelTableData } from 'components/Project/ProjectTabs/CyclesPayoutsTab/ConfigurationPanel' -import { useJBRuleset, useJBRulesetMetadata, useJBTokenContext } from 'juice-sdk-react' -import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' -import { useV4FormatConfigurationTokenSection } from './useV4FormatConfigurationTokenSection' - -export const useV4TokenSection = ( - type: 'current' | 'upcoming', -): ConfigurationPanelTableData => { - const { token } = useJBTokenContext() - const tokenSymbol = token?.data?.symbol - - const { data: ruleset } = useJBRuleset() - const { data: rulesetMetadata } = useJBRulesetMetadata() - const { - ruleset: upcomingRuleset, - rulesetMetadata: upcomingRulesetMetadata, - isLoading: upcomingRulesetLoading - } = useJBUpcomingRuleset() - - return useV4FormatConfigurationTokenSection({ - ruleset, - rulesetMetadata, - tokenSymbol, - upcomingRuleset, - upcomingRulesetMetadata, - upcomingRulesetLoading, - // Hide upcoming info from current section. - ...(type === 'current' && { - upcomingRuleset: null, - upcomingRulesetMetadata: null, - }), - }) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4TreasuryStats.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4TreasuryStats.tsx deleted file mode 100644 index 390ca45ed8..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4CyclesPayoutsPanel/hooks/useV4TreasuryStats.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { t } from '@lingui/macro' -import { NativeTokenValue, useJBRulesetMetadata, useNativeTokenSurplus } from 'juice-sdk-react' -import { usePayoutLimit } from 'packages/v4/hooks/usePayoutLimit' -import { useV4BalanceOfNativeTerminal } from 'packages/v4/hooks/useV4BalanceOfNativeTerminal' -import { MAX_PAYOUT_LIMIT } from 'packages/v4/utils/math' -import { useMemo } from 'react' -import { useV4DistributableAmount } from './useV4DistributableAmount' - -export const useV4TreasuryStats = () => { - const { data: rulesetMetadata } = useJBRulesetMetadata() - const { distributableAmount } = useV4DistributableAmount() - const { data: surplusInNativeToken } = useNativeTokenSurplus() - - const { data: _treasuryBalance } = useV4BalanceOfNativeTerminal() - - const { data: payoutLimit } = usePayoutLimit() - - const treasuryBalance = useMemo(() => { - if (!_treasuryBalance) return undefined - - return ( - - ) - }, [_treasuryBalance]) - - - const surplus = useMemo(() => { - if (payoutLimit && payoutLimit.amount === MAX_PAYOUT_LIMIT) return t`No surplus` - - return ( - - ) - }, [ - surplusInNativeToken, - payoutLimit, - ]) - - const availableToPayout = useMemo(() => { - return ( - - ) - }, [distributableAmount]) - return { - treasuryBalance, - availableToPayout, - surplus, - redemptionRate: rulesetMetadata?.redemptionRate, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx index 120f3a7376..d7dbf95334 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx @@ -2,12 +2,16 @@ import { Tab } from '@headlessui/react' import { t } from '@lingui/macro' import { ProjectTab } from 'components/Project/ProjectTabs/ProjectTab' import { useOnScreen } from 'hooks/useOnScreen' -import { Fragment, useEffect, useMemo, useRef, useState } from 'react' +import { useProjectPageQueries } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectPageQueries' +import { + Fragment, + useEffect, + useMemo, + useRef, + useState, +} from 'react' import { twMerge } from 'tailwind-merge' -import { useProjectPageQueries } from '../hooks/useProjectPageQueries' -import { V4ActivityPanel } from './V4ActivityPanel/V4ActivityPanel' -import { V4CyclesPayoutsPanel } from './V4CyclesPayoutsPanel/V4CyclesPayoutsPanel' -import { V4TokensPanel } from './V4TokensPanel/V4TokensPanel' +import { V4CyclesPayoutsPanel } from './CyclesPayoutsPanel/V4CyclesPayoutsPanel' type ProjectTabConfig = { id: string @@ -44,14 +48,14 @@ export const V4ProjectTabs = ({ className }: { className?: string }) => { const tabs: ProjectTabConfig[] = useMemo( () => [ - { id: 'activity', name: t`Activity`, panel: }, + { id: 'activity', name: t`Activity`, panel: <> }, { id: 'about', name: t`About`, panel: <> }, { id: 'cycle_payouts', name: t`Cycles & Payouts`, panel: , }, - { id: 'tokens', name: t`Tokens`, panel: }, + { id: 'tokens', name: t`Tokens`, panel: <> }, ], [], ) diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4DistributeReservedTokensModal.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4DistributeReservedTokensModal.tsx deleted file mode 100644 index 6e48caa12a..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4DistributeReservedTokensModal.tsx +++ /dev/null @@ -1,122 +0,0 @@ -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 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' - -export default function V4DistributeReservedTokensModal({ - open, - onCancel, - onConfirmed, -}: { - open?: boolean - onCancel?: VoidFunction - onConfirmed?: VoidFunction -}) { - const { projectId } = useJBContractContext() - const { splits: reservedTokensSplits } = useV4ReservedSplits() - const { data: projectOwnerAddress } = useProjectOwnerOf() - 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], - }) - async function distributeReservedTokens() { - setLoading(true) - - // const txSuccessful = await distributeReservedTokensTx( - // {}, - // { - // onDone: () => { - // setTransactionPending(true) - // }, - // onConfirmed: () => { - // setLoading(false) - // setTransactionPending(false) - // onConfirmed?.() - // }, - // }, - // ) - - // if (!txSuccessful) { - setLoading(false) - setTransactionPending(false) - // } - } - - const reservedTokensFormatted = formatWad(totalCreditSupply, { precision: 0 }) - - const tokenTextPlural = tokenSymbolText({ - tokenSymbol, - capitalize: false, - plural: true, - }) - - const tokenTextSingular = tokenSymbolText({ - tokenSymbol, - capitalize: true, - plural: false, - }) - - return ( - Send reserved {tokenTextPlural}} - open={open} - onOk={() => distributeReservedTokens()} - okText={t`Send ${tokenTextPlural}`} - connectWalletText={t`Connect wallet to send reserved ${tokenTextPlural}`} - confirmLoading={loading} - transactionPending={transactionPending} - onCancel={onCancel} - okButtonProps={{ disabled: !(totalCreditSupply && totalCreditSupply > 0n) }} - width={640} - centered={true} - > -
-
- - Reserved {tokenTextPlural}:{' '} - - {reservedTokensFormatted} {tokenTextPlural} - - -
-
-

- {tokenTextSingular} recipients -

- - {reservedTokensSplits?.length === 0 ? ( -

- - The project owner is the only reserved token recipient. Any - reserved tokens sent out this cycle will go to them. - -

- ) : null} - - -
-
-
- ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ExportReservedTokensCsvItem.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ExportReservedTokensCsvItem.tsx deleted file mode 100644 index cc2677ba5b..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ExportReservedTokensCsvItem.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ArrowUpTrayIcon } from '@heroicons/react/24/outline' -import { Trans } from '@lingui/macro' -import { useJBRuleset } from 'juice-sdk-react' -import { useV4ReservedSplits } from 'packages/v4/hooks/useV4ReservedSplits' -import { useV4ExportSplitsToCsv } from '../V4CyclesPayoutsPanel/hooks/useV4ExportSplitsToCsv' - -export const V4ExportReservedTokensCsvItem = () => { - const { splits: reservedTokensSplits } = useV4ReservedSplits() - const { data: ruleset } = useJBRuleset() - const { exportSplitsToCsv } = useV4ExportSplitsToCsv( - reservedTokensSplits ?? [], - 'reserved-tokens', - Number(ruleset?.cycleNumber), - ) - if (!reservedTokensSplits?.length) return null - - return ( - - - - Export tokens CSV - - - ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ReservedTokensSubPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ReservedTokensSubPanel.tsx deleted file mode 100644 index fe86053797..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4ReservedTokensSubPanel.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { Trans, t } from '@lingui/macro' -import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard' -import { reservedTokensTooltip } from 'components/Project/ProjectTabs/TokensPanelTooltips' -import { twMerge } from 'tailwind-merge' -import { V4ProjectAllocationRow } from '../V4CyclesPayoutsPanel/V4ProjectAllocationRow' -import { useV4ReservedTokensSubPanel } from './hooks/useV4ReservedTokensSubPanel' -import { V4ExportReservedTokensCsvItem } from './V4ExportReservedTokensCsvItem' -import { V4SendReservedTokensButton } from './V4SendReservedTokensButton' - -export const V4ReservedTokensSubPanel = ({ - className, -}: { - className?: string -}) => { - const { reservedList, totalCreditSupply, reservedPercent } = - useV4ReservedTokensSubPanel() - - const reservedPercentTooltip = ( - - {reservedPercent} of token issuance is set aside for the recipients below. - - ) - - return ( -
-

- Reserved tokens -

-
-
- {totalCreditSupply} - ) : ( -
- ) - } - tooltip={reservedTokensTooltip} - /> - -
- {reservedPercent && - totalCreditSupply && - reservedPercent !== '0' ? ( - - {totalCreditSupply || - reservedPercent || - (reservedList && reservedList.length > 1) ? ( - <> -
- {reservedList - ? reservedList.map(props => ( - - )) - : null} -
- - - - ) : ( -
- - No distributable reserved tokens have been configured for this - project. - -
- )} -
- ) : null} -
-
- ) -} - -const kebabMenuItems = [ - { - id: 'export', - component: , - }, -] diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4SendReservedTokensButton.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4SendReservedTokensButton.tsx deleted file mode 100644 index 879843185c..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4SendReservedTokensButton.tsx +++ /dev/null @@ -1,52 +0,0 @@ -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 { twMerge } from 'tailwind-merge' -import { reloadWindow } from 'utils/windowUtils' -import V4DistributeReservedTokensModal from './V4DistributeReservedTokensModal' - -export const V4SendReservedTokensButton = ({ - className, - containerClassName, -}: { - className?: string - containerClassName?: string -}) => { - const { projectId } = useJBContractContext() - const { data: totalCreditSupply, isLoading: totalCreditSupplyLoading } = useReadJbTokensTotalCreditSupplyOf({ - args: [projectId], - }) - const [open, setOpen] = useState(false) - const openModal = useCallback(() => setOpen(true), []) - const closeModal = useCallback(() => setOpen(false), []) - - const distributeButtonDisabled = useMemo(() => { - return (totalCreditSupply ?? 0n) === 0n - }, [totalCreditSupply]) - - return ( - No reserved tokens to send.} - open={distributeButtonDisabled ? undefined : false} - className={containerClassName} - > - - - - ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4TokensPanel.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4TokensPanel.tsx deleted file mode 100644 index 9e86976584..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/V4TokensPanel.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import { Trans, t } from '@lingui/macro' -import EthereumAddress from 'components/EthereumAddress' -import { TitleDescriptionDisplayCard } from 'components/Project/ProjectTabs/TitleDescriptionDisplayCard' -// import { TokenHoldersModal } from '../TokenHoldersModal/TokenHoldersModal' -// import { MigrateTokensButton } from './components/MigrateTokensButton' -// import { RedeemTokensButton } from './components/RedeemTokensButton' -// import { ReservedTokensSubPanel } from './components/ReservedTokensSubPanel' -// import { TokenRedemptionCallout } from './components/TokenRedemptionCallout' -// import { TransferUnclaimedTokensModalWrapper } from './components/TransferUnclaimedTokensModalWrapper' -import { AddTokenToMetamaskButton } from 'components/buttons/AddTokenToMetamaskButton' -import { IssueErc20TokenButton } from 'components/buttons/IssueErc20TokenButton' -import { V4TokenHoldersModal } from 'packages/v4/components/V4TokenHoldersModal' -import { useCallback, useState } from 'react' -import { reloadWindow } from 'utils/windowUtils' -import { useV4TokensPanel } from './hooks/useV4TokensPanel' -import { useV4YourBalanceMenuItems } from './hooks/useV4YourBalanceMenuItems' -import { V4ReservedTokensSubPanel } from './V4ReservedTokensSubPanel' - -export const V4TokensPanel = () => { - const { - userTokenBalance, - userTokenBalanceLoading, - // userLegacyTokenBalance, - // projectHasLegacyTokens, - // userV1ClaimedBalance, - projectToken, - totalSupply, - } = useV4TokensPanel() - - const [tokenHolderModalOpen, setTokenHolderModalOpen] = useState(false) - const openTokenHolderModal = useCallback( - () => setTokenHolderModalOpen(true), - [], - ) - const closeTokenHolderModal = useCallback( - () => setTokenHolderModalOpen(false), - [], - ) - - const { - items, - // redeemModalVisible, - // setRedeemModalVisible, - // claimTokensModalVisible, - setClaimTokensModalVisible, - // mintModalVisible, - // setMintModalVisible, - // transferUnclaimedTokensModalVisible, - // setTransferUnclaimedTokensModalVisible, - } = useV4YourBalanceMenuItems() - - return ( - <> -
-
-

Tokens

-
- - {/* */} - -
- {!userTokenBalanceLoading && userTokenBalance !== undefined && ( - - {userTokenBalance.format()} tokens -
- {/* {projectHasErc20Token && ( - - )} */} - {/* */} -
- - } - kebabMenu={userTokenBalance.value > 0n ? { - items - } : undefined} - /> - )} - - {/* {projectHasLegacyTokens && userLegacyTokenBalance?.gt(0) ? ( - - - -
- } - /> - ) : null} */} - -
-
- - - {totalSupply.format()} {projectToken} - - } - /> -
- - View token holders - -
- - -
-
- - {/* setRedeemModalVisible(false)} - onConfirmed={reloadWindow} - /> - setClaimTokensModalVisible(false)} - onConfirmed={reloadWindow} - /> - setMintModalVisible(false)} - onConfirmed={reloadWindow} - /> - setTransferUnclaimedTokensModalVisible(false)} - onConfirmed={reloadWindow} - /> */} - - ) -} - -const ProjectTokenCard = () => { - const { - projectToken, - projectTokenAddress, - projectHasErc20Token, - canCreateErc20Token, - } = useV4TokensPanel() - return ( - -
- {projectHasErc20Token ? projectToken : t`Token`} - - {projectHasErc20Token && ( - - - - )} -
- {projectTokenAddress && projectHasErc20Token && ( - - )} - {canCreateErc20Token && ( - - )} - - } - /> - ) -} - -const ProjectTokenBadge = () => { - const { projectHasErc20Token } = useV4TokensPanel() - return ( - - {projectHasErc20Token ? 'ERC-20' : t`Juicebox native`} - - ) -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4BalanceMenuItemsUserFlags.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4BalanceMenuItemsUserFlags.ts deleted file mode 100644 index f34cdd73b0..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4BalanceMenuItemsUserFlags.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { useWallet } from 'hooks/Wallet' -import { useJBContractContext, useJBRulesetMetadata, useNativeTokenSurplus, useReadJbTokensCreditBalanceOf, useReadJbTokensTokenOf } from 'juice-sdk-react' -import { useV4WalletHasPermission } from 'packages/v4/hooks/useV4WalletHasPermission' -import { V4OperatorPermission } from 'packages/v4/models/v4Permissions' -import { useMemo } from 'react' -import { isZeroAddress } from 'utils/address' -import { zeroAddress } from 'viem' - -export const useV4BalanceMenuItemsUserFlags = () => { - const { data: rulesetMetadata } = useJBRulesetMetadata() - const { data: tokenAddress } = useReadJbTokensTokenOf() - const { data: surplusInNativeToken } = useNativeTokenSurplus() - const { userAddress } = useWallet() - - const { projectId } = useJBContractContext() - const isDev = useMemo(() => process.env.NODE_ENV === 'development', []) - - const userHasMintPermission = useV4WalletHasPermission( - V4OperatorPermission.MINT_TOKENS, - ) - const hasOverflow = useMemo( - () => !!(surplusInNativeToken && surplusInNativeToken > 0n), - [surplusInNativeToken], - ) - const redemptionRateIsZero = !!(rulesetMetadata && rulesetMetadata.redemptionRate.value === 0n) - const redeemDisabled = useMemo( - () => !hasOverflow || redemptionRateIsZero, - [hasOverflow, redemptionRateIsZero], - ) - const projectHasIssuedTokens = useMemo( - () => !!tokenAddress && !isZeroAddress(tokenAddress), - [tokenAddress], - ) - const projectAllowsMint = useMemo( - () => !!rulesetMetadata?.allowOwnerMinting, - [rulesetMetadata], - ) - const { data: creditBalance } = useReadJbTokensCreditBalanceOf({ // previously `unclaimedTokenBalance` - args: [ - userAddress ?? zeroAddress, - projectId - ] - }) - - const canBurnTokens = useMemo( - () => redeemDisabled || isDev, - [isDev, redeemDisabled], - ) - - const canClaimErcTokens = useMemo( - () => projectHasIssuedTokens || isDev, - [isDev, projectHasIssuedTokens], - ) - - const canMintTokens = useMemo( - () => (projectAllowsMint && userHasMintPermission) || isDev, - [isDev, projectAllowsMint, userHasMintPermission], - ) - const creditBalanceZero = !!(creditBalance && creditBalance > 0n) - const canTransferTokens = useMemo( - () => creditBalanceZero || isDev, - [creditBalanceZero, isDev], - ) - - return { - canBurnTokens, - canClaimErcTokens, - canMintTokens, - canTransferTokens, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4ReservedTokensSubPanel.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4ReservedTokensSubPanel.ts deleted file mode 100644 index a1c1787d52..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4ReservedTokensSubPanel.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { WeiPerEther } from '@ethersproject/constants' -import { SplitPortion, SPLITS_TOTAL_PERCENT } from 'juice-sdk-core' -import { - useJBContractContext, - useJBRulesetMetadata, - useReadJbTokensTotalCreditSupplyOf, -} 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 { data: projectOwnerAddress } = useProjectOwnerOf() - const { splits: reservedTokensSplits } = useV4ReservedSplits() - const { data: rulesetMetadata } = useJBRulesetMetadata() - const reservedPercent = `${rulesetMetadata?.reservedPercent.formatPercentage()}%` - - const { data: totalCreditSupply } = useReadJbTokensTotalCreditSupplyOf({ - args: [projectId], - }) - - const reservedList = useMemo(() => { - if (!projectOwnerAddress || !projectId || !reservedTokensSplits) return - // If there aren't explicitly defined splits, all reserved tokens go to this project. - if (reservedTokensSplits?.length === 0) - return [ - { - projectId: Number(projectId), - address: projectOwnerAddress!, - percent: `${new SplitPortion( - SPLITS_TOTAL_PERCENT, - ).formatPercentage()}%`, - }, - ] - - // If the splits don't add up to 100%, remaining tokens go to this project. - let splitsPercentTotal = 0 - const processedSplits = reservedTokensSplits - .sort((a, b) => Number(b.percent) - Number(a.percent)) - .map(split => { - assert(split.beneficiary, 'Beneficiary must be defined') - splitsPercentTotal += Number(split.percent.value) - - return { - projectId: Number(split.projectId), - address: split.beneficiary!, - percent: `${split.percent.formatPercentage()}%`, - } - }) - - const remainingPercentage = SPLITS_TOTAL_PERCENT - splitsPercentTotal - - // Check if this project is already one of the splits. - if (!(remainingPercentage === 0)) { - const projectSplitIndex = processedSplits.findIndex( - v => v.projectId === Number(projectId), - ) - if (projectSplitIndex != -1) - // If it is, increase its split percentage to bring the total to 100%. - processedSplits[projectSplitIndex].percent = `${new SplitPortion( - remainingPercentage + - Number(reservedTokensSplits[projectSplitIndex].percent.value), - ).formatPercentage()}%` - // If it isn't, add a split at the beginning which brings the total percentage to 100%. - else - processedSplits.unshift({ - projectId: Number(projectId), - address: projectOwnerAddress!, - percent: `${new SplitPortion( - remainingPercentage, - ).formatPercentage()}%`, - }) - } - - return processedSplits - }, [reservedTokensSplits, projectOwnerAddress, projectId]) - - const totalCreditSupplyFormatted = useMemo(() => { - if (totalCreditSupply === undefined) return - return formatAmount(Number(totalCreditSupply / WeiPerEther.toBigInt()), { - maximumFractionDigits: 2, - }) - }, [totalCreditSupply]) - - return { - reservedList, - totalCreditSupply: totalCreditSupplyFormatted, - reservedPercent, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4TokensPanel.ts b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4TokensPanel.ts deleted file mode 100644 index 994087fc8b..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4TokensPanel.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { useWallet } from 'hooks/Wallet' -import { JBProjectToken } from 'juice-sdk-core' -import { useJBContractContext, useJBTokenContext, useReadJbTokensTotalBalanceOf } from 'juice-sdk-react' -import { useV4TotalTokenSupply } from 'packages/v4/hooks/useV4TotalTokenSupply' -import { useV4WalletHasPermission } from 'packages/v4/hooks/useV4WalletHasPermission' -import { V4OperatorPermission } from 'packages/v4/models/v4Permissions' -import { useMemo } from 'react' -import { isZeroAddress } from 'utils/address' -import { tokenSymbolText } from 'utils/tokenSymbolText' -import { zeroAddress } from 'viem' - -export const useV4TokensPanel = () => { - const { projectId } = useJBContractContext() - const { userAddress } = useWallet() - const { token } = useJBTokenContext() - const tokenAddress = token?.data?.address - - const { data: _totalTokenSupply } = useV4TotalTokenSupply() - - const projectToken = tokenSymbolText({ - tokenSymbol: token?.data?.symbol, - capitalize: false, - plural: true, - }) - // const projectHasLegacyTokens = useProjectHasLegacyTokens() - const hasDeployErc20Permission = useV4WalletHasPermission( - V4OperatorPermission.DEPLOY_ERC20, - ) - const projectHasErc20Token = Boolean(tokenAddress && !isZeroAddress(tokenAddress)) - - const { data: _userTokenBalance, isLoading: userTokenBalanceLoading } = - useReadJbTokensTotalBalanceOf({ - args: [ - userAddress ?? zeroAddress, - projectId - ] - }) - const userTokenBalance = useMemo(() => { - if (_userTokenBalance === undefined) return - return new JBProjectToken(_userTokenBalance ?? 0n) - }, [_userTokenBalance]) - - // const { totalLegacyTokenBalance, v1ClaimedBalance } = - // useTotalLegacyTokenBalance({ projectId }) - - const totalTokenSupply = useMemo(() => { - return new JBProjectToken(_totalTokenSupply ?? 0n) - }, [_totalTokenSupply]) - - const canCreateErc20Token = useMemo(() => { - return !projectHasErc20Token && hasDeployErc20Permission - }, [hasDeployErc20Permission, projectHasErc20Token]) - - return { - userTokenBalance, - userTokenBalanceLoading, - // userLegacyTokenBalance: totalLegacyTokenBalance, - // projectHasLegacyTokens, - // userV1ClaimedBalance: v1ClaimedBalance, - projectToken, - projectTokenAddress: tokenAddress, - totalSupply: totalTokenSupply, - projectHasErc20Token, - canCreateErc20Token, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4YourBalanceMenuItems.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4YourBalanceMenuItems.tsx deleted file mode 100644 index d9997ecc63..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4TokensPanel/hooks/useV4YourBalanceMenuItems.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { - ArrowRightOnRectangleIcon, - FireIcon, - PlusCircleIcon, - ReceiptRefundIcon, -} from '@heroicons/react/24/outline' -import { t } from '@lingui/macro' -import { PopupMenuItem } from 'components/ui/PopupMenu' -import { ReactNode, useMemo, useState } from 'react' -import { useV4BalanceMenuItemsUserFlags } from './useV4BalanceMenuItemsUserFlags' - -export const useV4YourBalanceMenuItems = () => { - const { canBurnTokens, canClaimErcTokens, canMintTokens, canTransferTokens } = - useV4BalanceMenuItemsUserFlags() - - const [redeemModalVisible, setRedeemModalVisible] = useState(false) - const [claimTokensModalVisible, setClaimTokensModalVisible] = useState(false) - const [mintModalVisible, setMintModalVisible] = useState(false) - const [ - transferUnclaimedTokensModalVisible, - setTransferUnclaimedTokensModalVisible, - ] = useState(false) - - const items = useMemo(() => { - const tokenMenuItems: PopupMenuItem[] = [] - if (canBurnTokens) { - tokenMenuItems.push({ - id: 'burn', - label: ( - } - /> - ), - onClick: () => setRedeemModalVisible(true), - }) - } - if (canClaimErcTokens) { - tokenMenuItems.push({ - id: 'claim', - label: ( - } - /> - ), - onClick: () => setClaimTokensModalVisible(true), - }) - } - if (canMintTokens) { - tokenMenuItems.push({ - id: 'mint', - label: ( - } - /> - ), - onClick: () => setMintModalVisible(true), - }) - } - if (canTransferTokens) { - tokenMenuItems.push({ - id: 'transfer', - label: ( - } - /> - ), - onClick: () => setTransferUnclaimedTokensModalVisible(true), - }) - } - return tokenMenuItems - }, [canBurnTokens, canClaimErcTokens, canMintTokens, canTransferTokens]) - - return { - items, - redeemModalVisible, - setRedeemModalVisible, - claimTokensModalVisible, - setClaimTokensModalVisible, - mintModalVisible, - setMintModalVisible, - transferUnclaimedTokensModalVisible, - setTransferUnclaimedTokensModalVisible, - } -} - -const TokenItemLabel = ({ - label, - icon, -}: { - label: ReactNode - icon: ReactNode -}) => ( - <> - {icon} - {label} - -) diff --git a/src/packages/v4/views/V4ProjectDashboard/hooks/useProjectPageQueries.ts b/src/packages/v4/views/V4ProjectDashboard/hooks/useProjectPageQueries.ts deleted file mode 100644 index 41ae2f2929..0000000000 --- a/src/packages/v4/views/V4ProjectDashboard/hooks/useProjectPageQueries.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { useRouter } from 'next/router' -import { V4CurrencyOption } from 'packages/v4/models/v4CurrencyOption' -import { useCallback, useMemo } from 'react' - -type ProjectPageTab = - | 'activity' - | 'about' - | 'nft_rewards' - | 'cycle_payouts' - | 'tokens' - -export type ProjectPayReceipt = { - totalAmount: { - amount: number - currency: V4CurrencyOption - } - tokensReceived: string - timestamp: Date - transactionHash: string | undefined - fromAddress: string - nfts: { - id: number - }[] -} - -export const useProjectPageQueries = () => { - const router = useRouter() - - const projectPageTab = router.query.tabid as ProjectPageTab | undefined - const projectPayReceiptString = router.query.payReceipt as string | undefined - - const projectPayReceipt = useMemo(() => { - if (!projectPayReceiptString) { - return undefined - } - - try { - const parsed = JSON.parse(projectPayReceiptString) as ProjectPayReceipt - return { - ...parsed, - timestamp: new Date(parsed.timestamp), - } - } catch (error) { - console.error('Failed to parse projectPayReceipt', error) - return undefined - } - }, [projectPayReceiptString]) - - const setProjectPageTab = useCallback( - (tabId: string) => { - router.replace( - { - pathname: router.pathname, - query: { ...router.query, tabid: tabId }, - }, - undefined, - { shallow: true }, - ) - }, - [router], - ) - - const setProjectPayReceipt = useCallback( - (payReceipt: ProjectPayReceipt | undefined) => { - router.replace( - { - pathname: router.pathname, - query: { - ...router.query, - payReceipt: payReceipt ? JSON.stringify(payReceipt) : undefined, - }, - }, - undefined, - { shallow: true }, - ) - }, - [router], - ) - - return { - projectPageTab, - setProjectPageTab, - projectPayReceipt, - setProjectPayReceipt, - } -} diff --git a/src/packages/v4/views/V4ProjectDashboard/hooks/useV4CurrentUpcomingSubPanel.ts b/src/packages/v4/views/V4ProjectDashboard/hooks/useV4CurrentUpcomingSubPanel.ts index 7561a68860..f397e2a84d 100644 --- a/src/packages/v4/views/V4ProjectDashboard/hooks/useV4CurrentUpcomingSubPanel.ts +++ b/src/packages/v4/views/V4ProjectDashboard/hooks/useV4CurrentUpcomingSubPanel.ts @@ -2,52 +2,54 @@ import { t } from '@lingui/macro' import { useJBRuleset, useReadJbRulesetsCurrentApprovalStatusForLatestRulesetOf, + useReadJbRulesetsLatestQueuedOf, } from 'juice-sdk-react' import { V4ApprovalStatus } from 'models/ballot' -import { useJBUpcomingRuleset } from 'packages/v4/hooks/useJBUpcomingRuleset' import { useMemo } from 'react' import { timeSecondsToDateString } from 'utils/timeSecondsToDateString' +import { useRulesetCountdown } from './useRulesetCountdown' export const useV4CurrentUpcomingSubPanel = (type: 'current' | 'upcoming') => { const { data: ruleset, isLoading: rulesetLoading } = useJBRuleset() - const { ruleset: latestUpcomingRuleset, isLoading: upcomingRulesetsLoading } = - useJBUpcomingRuleset() + const { data: _latestQueuedRuleset, isLoading: queuedRulesetsLoading } = + useReadJbRulesetsLatestQueuedOf() + const { timeRemainingText } = useRulesetCountdown() + const latestQueuedRuleset = _latestQueuedRuleset?.[0] const { data: approvalStatus } = useReadJbRulesetsCurrentApprovalStatusForLatestRulesetOf() + const rulesetNumber = useMemo(() => { if (type === 'current') { return Number(ruleset?.cycleNumber) } - return latestUpcomingRuleset?.cycleNumber - ? Number(latestUpcomingRuleset.cycleNumber) - : ruleset?.cycleNumber - ? ruleset.cycleNumber + 1 + return latestQueuedRuleset?.cycleNumber + ? Number(latestQueuedRuleset.cycleNumber) : undefined - }, [ruleset?.cycleNumber, type, latestUpcomingRuleset?.cycleNumber]) + }, [ruleset?.cycleNumber, type, latestQueuedRuleset?.cycleNumber]) const rulesetUnlocked = useMemo(() => { if (type === 'current') { - return ruleset?.duration === 0 ?? true + return ruleset?.duration === 0n ?? true } - return latestUpcomingRuleset?.duration == 0 ?? true - }, [ruleset?.duration, type, latestUpcomingRuleset?.duration]) + return latestQueuedRuleset?.duration == 0n ?? true + }, [ruleset?.duration, type, latestQueuedRuleset?.duration]) const upcomingRulesetLength = useMemo(() => { - if (!latestUpcomingRuleset) - return timeSecondsToDateString(Number(ruleset?.duration), 'short') + if (!latestQueuedRuleset) return if (rulesetUnlocked) return '-' return timeSecondsToDateString( - Number(latestUpcomingRuleset.duration), + Number(latestQueuedRuleset.duration), 'short', ) - }, [rulesetUnlocked, latestUpcomingRuleset, ruleset]) + }, [rulesetUnlocked, latestQueuedRuleset]) /** Determines if the CURRENT cycle is unlocked. * This is used to check if the upcoming cycle can start at any time. */ - const currentRulesetUnlocked = ruleset?.duration === 0 ?? true + const currentRulesetUnlocked = ruleset?.duration === 0n ?? true const status = rulesetUnlocked ? t`Unlocked` : t`Locked` + const remainingTime = rulesetUnlocked ? '-' : timeRemainingText // Short circuit current for faster loading if (type === 'current') { @@ -57,10 +59,11 @@ export const useV4CurrentUpcomingSubPanel = (type: 'current' | 'upcoming') => { type, rulesetNumber, status, + remainingTime, } } - if (rulesetLoading || upcomingRulesetsLoading) + if (rulesetLoading || queuedRulesetsLoading) return { loading: true, type, @@ -77,7 +80,7 @@ export const useV4CurrentUpcomingSubPanel = (type: 'current' | 'upcoming') => { hasPendingConfiguration: /** * If a ruleset is unlocked, it may have a pending change. - * The only way it would, is if the approval status of the latestUpcomingRuleset is `approved`. + * The only way it would, is if the approval status of the latestQueuedRuleset is `approved`. */ rulesetUnlocked && typeof approvalStatus !== 'undefined' && diff --git a/src/packages/v4/views/V4ProjectDashboard/hooks/useV4ProjectHeader.ts b/src/packages/v4/views/V4ProjectDashboard/hooks/useV4ProjectHeader.ts index a09560f02b..ea630d929a 100644 --- a/src/packages/v4/views/V4ProjectDashboard/hooks/useV4ProjectHeader.ts +++ b/src/packages/v4/views/V4ProjectDashboard/hooks/useV4ProjectHeader.ts @@ -4,13 +4,13 @@ import { useProjectTrendingPercentageIncrease } from 'hooks/useProjectTrendingPe import { SubtitleType, useSubtitle } from 'hooks/useSubtitle' import { useJBContractContext, - useJBProjectMetadataContext + useJBProjectMetadataContext, + useReadJbProjectsOwnerOf, } from 'juice-sdk-react' import { GnosisSafe } from 'models/safe' import { useRouter } from 'next/router' import { ProjectsDocument } from 'packages/v4/graphql/client/graphql' import { useSubgraphQuery } from 'packages/v4/graphql/useSubgraphQuery' -import useProjectOwnerOf from 'packages/v4/hooks/useV4ProjectOwnerOf' export interface ProjectHeaderData { title: string | undefined subtitle: { text: string; type: SubtitleType } | undefined @@ -31,7 +31,9 @@ export const useV4ProjectHeader = (): ProjectHeaderData => { const { metadata } = useJBProjectMetadataContext() const projectMetadata = metadata?.data - const { data: projectOwnerAddress } = useProjectOwnerOf() + const { data: projectOwnerAddress } = useReadJbProjectsOwnerOf({ + args: [projectId], + }) const projectIdNum = parseInt(projectId.toString()) diff --git a/src/redux/hooks/useEditingPayoutSplits.ts b/src/redux/hooks/useEditingPayoutSplits.ts index 67efa9dfdd..f9c2c65f9e 100644 --- a/src/redux/hooks/useEditingPayoutSplits.ts +++ b/src/redux/hooks/useEditingPayoutSplits.ts @@ -1,4 +1,4 @@ -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { useCallback } from 'react' import { useAppDispatch } from 'redux/hooks/useAppDispatch' import { useAppSelector } from 'redux/hooks/useAppSelector' diff --git a/src/redux/hooks/useEditingReservedTokensSplits.ts b/src/redux/hooks/useEditingReservedTokensSplits.ts index 7899e7d899..c45f1b85a2 100644 --- a/src/redux/hooks/useEditingReservedTokensSplits.ts +++ b/src/redux/hooks/useEditingReservedTokensSplits.ts @@ -1,4 +1,4 @@ -import { Split } from 'packages/v2v3/models/splits' +import { Split } from 'models/splits' import { useCallback } from 'react' import { useAppDispatch } from 'redux/hooks/useAppDispatch' import { useAppSelector } from 'redux/hooks/useAppSelector' diff --git a/src/redux/slices/editingV2Project/editingV2Project.ts b/src/redux/slices/editingV2Project/editingV2Project.ts index 4cd4d38807..4c2cdc4799 100644 --- a/src/redux/slices/editingV2Project/editingV2Project.ts +++ b/src/redux/slices/editingV2Project/editingV2Project.ts @@ -12,9 +12,9 @@ import { PayoutsSelection } from 'models/payoutsSelection' import { ProjectTagName } from 'models/project-tags' import { ProjectTokensSelection } from 'models/projectTokenSelection' import { ReconfigurationStrategy } from 'models/reconfigurationStrategy' +import { Split } from 'models/splits' import { TreasurySelection } from 'models/treasurySelection' import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' -import { Split } from 'packages/v2v3/models/splits' import { SerializedV2V3FundAccessConstraint, SerializedV2V3FundingCycleData, diff --git a/src/redux/slices/editingV2Project/types.ts b/src/redux/slices/editingV2Project/types.ts index f6c4e541c3..7b72d90658 100644 --- a/src/redux/slices/editingV2Project/types.ts +++ b/src/redux/slices/editingV2Project/types.ts @@ -11,12 +11,12 @@ import { PayoutsSelection } from 'models/payoutsSelection' import { ProjectMetadata } from 'models/projectMetadata' import { ProjectTokensSelection } from 'models/projectTokenSelection' import { ReconfigurationStrategy } from 'models/reconfigurationStrategy' -import { TreasurySelection } from 'models/treasurySelection' -import { NftPricingContext } from 'packages/v2v3/hooks/JB721Delegate/contractReader/useNftCollectionPricingContext' import { ETHPayoutGroupedSplits, ReservedTokensGroupedSplits, -} from 'packages/v2v3/models/splits' +} from 'models/splits' +import { TreasurySelection } from 'models/treasurySelection' +import { NftPricingContext } from 'packages/v2v3/hooks/JB721Delegate/contractReader/useNftCollectionPricingContext' import { SerializedV2V3FundAccessConstraint, SerializedV2V3FundingCycleData, diff --git a/src/utils/antdRules/allocationInputAlreadyExistsRule.ts b/src/utils/antdRules/allocationInputAlreadyExistsRule.ts index d5af4fa6aa..e5bf32b16c 100644 --- a/src/utils/antdRules/allocationInputAlreadyExistsRule.ts +++ b/src/utils/antdRules/allocationInputAlreadyExistsRule.ts @@ -1,6 +1,6 @@ import { RuleObject } from 'antd/lib/form' import isEqual from 'lodash/isEqual' -import { projectIdToHex } from 'packages/v2v3/utils/v2v3Splits' +import { projectIdToHex } from 'utils/splits' /** * Rule is the same as {@link inputAlreadyExistsRule}, however will allow for diff --git a/src/utils/csv.ts b/src/utils/csv.ts index d1c1da0026..c0c8b856e4 100644 --- a/src/utils/csv.ts +++ b/src/utils/csv.ts @@ -1,7 +1,7 @@ import { BigNumber } from 'ethers' import { getAddress } from 'ethers/lib/utils' +import { Split } from 'models/splits' import { PayoutMod, TicketMod } from 'packages/v1/models/mods' -import { Split } from 'packages/v2v3/models/splits' import { splitPercentFrom } from 'packages/v2v3/utils/math' import { percentToPermyriad } from './format/formatNumber' diff --git a/src/utils/format/formatTime.ts b/src/utils/format/formatTime.ts index 5c76fa4ce6..8d8a785d0b 100644 --- a/src/utils/format/formatTime.ts +++ b/src/utils/format/formatTime.ts @@ -125,9 +125,9 @@ export const deriveDurationOption = ( ) } -export const formatTime = (timestamp: number | undefined) => { - if (timestamp === undefined || timestamp === 0) return undefined; - const timeDate = new Date(timestamp * 1000); +export const formatTime = (timestamp: bigint | undefined) => { + if (timestamp === undefined || timestamp === 0n) return undefined; + const timeDate = new Date(Number(timestamp) * 1000); const isoDateString = timeDate.toISOString().split('T')[0]; const formatOptions: Intl.DateTimeFormatOptions = { weekday: 'long', diff --git a/src/utils/math.ts b/src/utils/math.ts index 76b4b34c8a..1c6674e9de 100644 --- a/src/utils/math.ts +++ b/src/utils/math.ts @@ -1,4 +1,3 @@ -import { ONE_BILLION } from 'constants/numbers' import { BigNumber } from 'ethers' export type WeightFunction = ( @@ -41,11 +40,3 @@ export const roundIfCloseToNextInteger = ( } return num } - -export const feeForAmount = ( - amountWad: bigint | undefined, - feePerBillion: bigint | undefined, -): bigint | undefined => { - if (!feePerBillion || !amountWad) return - return amountWad * feePerBillion / BigInt(ONE_BILLION) -} diff --git a/src/packages/v2v3/utils/splitToAllocation.ts b/src/utils/splitToAllocation.ts similarity index 83% rename from src/packages/v2v3/utils/splitToAllocation.ts rename to src/utils/splitToAllocation.ts index b6cfc0e014..4b6734e9c2 100644 --- a/src/packages/v2v3/utils/splitToAllocation.ts +++ b/src/utils/splitToAllocation.ts @@ -1,10 +1,10 @@ +import { defaultSplit, Split } from 'models/splits' import { AllocationSplit } from 'packages/v2v3/components/shared/Allocation/Allocation' -import { defaultSplit, Split } from 'packages/v2v3/models/splits' import { preciseFormatSplitPercent, splitPercentFrom, } from 'packages/v2v3/utils/math' -import { sanitizeSplit } from 'packages/v2v3/utils/v2v3Splits' +import { sanitizeSplit } from 'utils/splits' export const splitToAllocation = (split: Split): AllocationSplit => { return { diff --git a/src/packages/v2v3/utils/v2v3Splits.ts b/src/utils/splits.ts similarity index 98% rename from src/packages/v2v3/utils/v2v3Splits.ts rename to src/utils/splits.ts index f58025e0ac..54aab5d8f1 100644 --- a/src/packages/v2v3/utils/v2v3Splits.ts +++ b/src/utils/splits.ts @@ -1,9 +1,9 @@ import { BigNumber, constants } from 'ethers' import isEqual from 'lodash/isEqual' +import { Split, SplitParams } from 'models/splits' import { isFiniteDistributionLimit } from 'packages/v2v3/utils/fundingCycle' import { SPLITS_TOTAL_PERCENT } from 'packages/v2v3/utils/math' -import { formatWad } from '../../../utils/format/formatNumber' -import { Split, SplitParams } from '../models/splits' +import { formatWad } from './format/formatNumber' // - true if the split has been removed (exists in old but not new), // - false if new (exists in new but not old) diff --git a/src/utils/tokenSymbolText.ts b/src/utils/tokenSymbolText.ts index 224b030885..b689d7b81d 100644 --- a/src/utils/tokenSymbolText.ts +++ b/src/utils/tokenSymbolText.ts @@ -12,13 +12,13 @@ export const tokenSymbolText = ({ plural?: boolean includeTokenWord?: boolean }) => { - const defaultTokenTextSingular = capitalize ? t`Token` : t`token` - const defaultTokenTextPlural = capitalize ? t`Tokens` : t`tokens` - const defaultTokenText = plural ? defaultTokenTextPlural : defaultTokenTextSingular + const tokenTextSingular = capitalize ? t`Token` : t`token` + const tokenTextPlural = capitalize ? t`Tokens` : t`tokens` + const tokenText = plural ? tokenTextPlural : tokenTextSingular if (includeTokenWord) { - return tokenSymbol ? `${tokenSymbol} ${defaultTokenText}` : defaultTokenText + return tokenSymbol ? `${tokenSymbol} ${tokenText}` : tokenText } - return tokenSymbol ?? defaultTokenText + return tokenSymbol ?? tokenText } diff --git a/yarn.lock b/yarn.lock index 3e9a6cf124..83441af08a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12603,18 +12603,18 @@ jsprim@^1.2.2: array-includes "^3.1.5" object.assign "^4.1.3" -juice-sdk-core@^10.0.3-alpha: - version "10.0.3-alpha" - resolved "https://registry.yarnpkg.com/juice-sdk-core/-/juice-sdk-core-10.0.3-alpha.tgz#dcd1afa2faa13f42559ced3b308e5e886892c7bd" - integrity sha512-E+Wx7zv/PCOWrY9Co62ilyHa/6ge44xltTsNeaF96a3d3jWhWlkLlrDBrHDnT48VvClw0LbnRWOFvaB662OyFg== +juice-sdk-core@^9.1.3-alpha: + version "9.1.3-alpha" + resolved "https://registry.yarnpkg.com/juice-sdk-core/-/juice-sdk-core-9.1.3-alpha.tgz#74f6859893f86836b306e40a1bc0097da027fa9a" + integrity sha512-UGPkzu1p6i6+ITsfM7ioSoqAyJJ/7GSa4T+WTUXfS5kVxD0dIImfZnhM3HpKtqhx6FpTnD4TGiOuVBbJeRXgtw== dependencies: bs58 "^5.0.0" fpnum "^1.0.0" -juice-sdk-react@^10.0.1-alpha: - version "10.0.1-alpha" - resolved "https://registry.yarnpkg.com/juice-sdk-react/-/juice-sdk-react-10.0.1-alpha.tgz#a1a9273292ed7b6f2f97e87e60c131b8cd53ee03" - integrity sha512-Lpdymh4bt2YBba2htXn/RB+Kohs1iMhu2mqzadwAtrYpsFz+dKkL5GFA1YLLSC96d31KSVJX/bArX46Uc5hnoA== +juice-sdk-react@^9.2.3-alpha: + version "9.2.3-alpha" + resolved "https://registry.yarnpkg.com/juice-sdk-react/-/juice-sdk-react-9.2.3-alpha.tgz#22a02fd969b001165e98c0f7283e6fbff611aa30" + integrity sha512-lLhI5C7MUs7E/k26nrrIOC5AcnKFffqOgKij8miQKuv2lQrsqKP2TCDP/lcAT6Ig+a2AciDmVpq8cmICtlxTxQ== juice@^10.0.0: version "10.0.0"