With unlocked rulesets, you can edit your project's rules at any time.
+
+ With locked rulesets, you can lock your project's rules for a period of time
+ (like 3 minutes, 2 years, or 14 days), helping you build trust with your
+ supporters.
+
+
+ This choice isn't permanent — you can switch between locked and unlocked
+ rulesets in the future.
+
+
+)
+
export const LOCKED_PAYOUT_EXPLANATION = (
If locked, this payout can't be edited or removed until the lock expires or
diff --git a/src/packages/v2v3/constants/juiceboxTokens.ts b/src/constants/juiceboxTokens.ts
similarity index 100%
rename from src/packages/v2v3/constants/juiceboxTokens.ts
rename to src/constants/juiceboxTokens.ts
diff --git a/src/lib/api/supabase/projects/api.ts b/src/lib/api/supabase/projects/api.ts
index b6e5679c2b..76b6193894 100644
--- a/src/lib/api/supabase/projects/api.ts
+++ b/src/lib/api/supabase/projects/api.ts
@@ -1,37 +1,65 @@
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { V2_BLOCKLISTED_PROJECTS } from 'constants/blocklist'
+import { PV_V4 } from 'constants/pv'
import {
DbProjectsDocument,
DbProjectsQuery,
Project,
QueryProjectsArgs,
} from 'generated/graphql'
+
+import { readNetwork } from 'constants/networks'
import { paginateDepleteQuery } from 'lib/apollo/paginateDepleteQuery'
-import { serverClient } from 'lib/apollo/serverClient'
+import { serverClient, v4SepoliaServerClient } from 'lib/apollo/serverClient'
import { DBProject, DBProjectQueryOpts, SGSBCompareKey } from 'models/dbProject'
import { Json } from 'models/json'
import { NextApiRequest, NextApiResponse } from 'next'
+import {
+ Dbv4ProjectsDocument,
+ Dbv4ProjectsQuery,
+} from 'packages/v4/graphql/client/graphql'
import { Database } from 'types/database.types'
import { isHardArchived } from 'utils/archived'
+import { getSubgraphIdForProject } from 'utils/graph'
import {
formatDBProjectRow,
formatSGProjectForDB,
parseDBProjectsRow,
} from 'utils/sgDbProjects'
+import { sepolia } from 'viem/chains'
import { dbProjects } from '../clients'
/**
* Query all projects from subgraph using apollo serverClient which is safe to use in edge runtime.
*/
export async function queryAllSGProjectsForServer() {
- const res = await paginateDepleteQuery({
- client: serverClient,
- document: DbProjectsDocument,
- })
+ const [res, resSepoliaV4] = await Promise.all([
+ paginateDepleteQuery({
+ client: serverClient,
+ document: DbProjectsDocument,
+ }),
+ paginateDepleteQuery({
+ client: v4SepoliaServerClient,
+ document: Dbv4ProjectsDocument,
+ }),
+ ])
// Response must be retyped with Json<>, because the serverClient does not perform the parsing expected by generated types
- const _res = res as unknown as Json>[]
-
- return _res.map(formatSGProjectForDB)
+ const _res = res.map(p => {
+ return {
+ ...p,
+ chainId: readNetwork.chainId,
+ }
+ }) as unknown as Json>[]
+ const _resSepoliaV4 = resSepoliaV4.map(p => {
+ return {
+ ...p,
+ id: getSubgraphIdForProject(PV_V4, p.projectId), // Patch in the subgraph ID for V4 projects (to be consitent with legacy subgraph)
+ pv: PV_V4, // Patch in the PV for V4 projects,
+ chainId: sepolia.id,
+ }
+ }) as unknown as Json>[]
+
+ return [..._res, ..._resSepoliaV4].map(formatSGProjectForDB)
}
/**
@@ -93,7 +121,6 @@ export async function queryDBProjects(
const pageSize = opts.pageSize ?? 20
// Only sort ascending if orderBy is defined and orderDirection is 'asc'
const ascending = opts.orderBy ? opts.orderDirection === 'asc' : false
-
const searchFilter = createSearchFilter(opts.text)
const supabase = createServerSupabaseClient({ req, res })
diff --git a/src/lib/apollo/serverClient.ts b/src/lib/apollo/serverClient.ts
index d023d1255e..7cf67db4c4 100644
--- a/src/lib/apollo/serverClient.ts
+++ b/src/lib/apollo/serverClient.ts
@@ -1,13 +1,21 @@
import { ApolloClient, InMemoryCache } from '@apollo/client'
-import { subgraphUri } from './subgraphUri'
+import { sepolia } from 'viem/chains'
+import { subgraphUri, v4SubgraphUri } from './subgraphUri'
/**
- * Unlike `client`, `serverClient` is safe to use in the edge runtime. However this client does not perform parsing on the response, meaning returned objects may not match the auto-generated types.
+ * Unlike `client`, `serverClient` is safe to use in the edge runtime.
+ * However, this client does not perform parsing on the response,
+ * meaning returned objects may not match the auto-generated types.
*/
const serverClient = new ApolloClient({
uri: subgraphUri(),
cache: new InMemoryCache(),
})
-export { serverClient }
+const v4SepoliaServerClient = new ApolloClient({
+ uri: v4SubgraphUri(sepolia.id),
+ cache: new InMemoryCache(),
+})
+
+export { serverClient, v4SepoliaServerClient }
diff --git a/src/lib/apollo/subgraphUri.ts b/src/lib/apollo/subgraphUri.ts
index d82b4506a2..cd94b8d3cf 100644
--- a/src/lib/apollo/subgraphUri.ts
+++ b/src/lib/apollo/subgraphUri.ts
@@ -1,4 +1,6 @@
+import { JBChainId } from 'juice-sdk-react'
import { isBrowser } from 'utils/isBrowser'
+import { sepolia } from 'viem/chains'
export const subgraphUri = () => {
let uri: string | undefined
@@ -20,5 +22,43 @@ export const subgraphUri = () => {
if (url.pathname.match(/graphql$/g)) {
return url.href.slice(0, url.href.lastIndexOf('/'))
}
+
+ return url.href
+}
+
+export const v4SubgraphUri = (chainId: JBChainId) => {
+ let uri: string | undefined
+
+ const env: {
+ [k in JBChainId]?: {
+ browserUrl?: string
+ serverUrl?: string
+ }
+ } = {
+ [sepolia.id]: {
+ browserUrl: process.env.NEXT_PUBLIC_V4_SEPOLIA_SUBGRAPH_URL,
+ serverUrl: process.env.V4_SEPOLIA_SUBGRAPH_URL,
+ },
+ } as const
+
+ if (isBrowser()) {
+ uri = env?.[chainId]?.browserUrl
+ if (!uri) {
+ throw new Error(
+ 'NEXT_PUBLIC_V4_SUBGRAPH_URL environment variable not defined',
+ )
+ }
+ } else {
+ uri = env?.[chainId]?.serverUrl
+ if (!uri) {
+ throw new Error('V4_SUBGRAPH_URL environment variable not defined')
+ }
+ }
+
+ const url = new URL(uri)
+ if (url.pathname.match(/graphql$/g)) {
+ return url.href.slice(0, url.href.lastIndexOf('/'))
+ }
+
return url.href
}
diff --git a/src/locales/messages.pot b/src/locales/messages.pot
index 1dfce92901..08f3ebe39c 100644
--- a/src/locales/messages.pot
+++ b/src/locales/messages.pot
@@ -92,9 +92,6 @@ msgstr ""
msgid "Claim {tokensLabel} as ERC-20"
msgstr ""
-msgid "Save project"
-msgstr ""
-
msgid "Total issuance"
msgstr ""
@@ -179,6 +176,9 @@ msgstr ""
msgid "Payout recipients:"
msgstr ""
+msgid "Your project's first ruleset will start on <0>{0} at {1}0>. Your project will be visible on <1>juicebox.money1> once you finish setting your project up, but supporters won't be able to pay or interact with it until the first ruleset begins."
+msgstr ""
+
msgid "Lock until"
msgstr ""
@@ -224,6 +224,9 @@ msgstr ""
msgid "Error downloading participants, try again."
msgstr ""
+msgid "Edit next ruleset"
+msgstr ""
+
msgid "Set a future date & time to start your project's first cycle."
msgstr ""
@@ -437,6 +440,9 @@ msgstr ""
msgid "Payer issuance rate"
msgstr ""
+msgid "None of your project's ETH can be paid out. All ETH will stay in your project for token redemptions or use in future rulesets."
+msgstr ""
+
msgid "Project Details"
msgstr ""
@@ -542,6 +548,9 @@ msgstr ""
msgid "This cycle has upcoming changes"
msgstr ""
+msgid "<0/> Your project's rules cannot be edited during the first ruleset."
+msgstr ""
+
msgid "While enabled, the project owner can change the project's <0>payment terminals0> at any time."
msgstr ""
@@ -557,6 +566,9 @@ msgstr ""
msgid "You would receive <0/>"
msgstr ""
+msgid "Simple token rules that will work for most projects. You can edit these rules in future rulesets."
+msgstr ""
+
msgid "The issuance reduction rate is disabled if you are using unlocked cycles (because they have no duration)."
msgstr ""
@@ -833,6 +845,9 @@ msgstr ""
msgid "Automated"
msgstr ""
+msgid "Ruleset"
+msgstr ""
+
msgid "Back to settings"
msgstr ""
@@ -1046,6 +1061,9 @@ msgstr ""
msgid "No overflow"
msgstr ""
+msgid "A fixed amount of ETH can be paid out from your project each ruleset. You can send specific ETH amounts (or ETH amounts based on USD values) to one or more recipients. Any remaining ETH will stay in your project for token redemptions or use in future rulesets."
+msgstr ""
+
msgid "Export tokens CSV"
msgstr ""
@@ -1181,6 +1199,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "The project's owner can edit the project's rules and start new rulesets at any time."
+msgstr ""
+
msgid "ETH transfers to project"
msgstr ""
@@ -1202,6 +1223,9 @@ msgstr ""
msgid "Fee from <0><1/>0>"
msgstr ""
+msgid "Reserved percent"
+msgstr ""
+
msgid "Later"
msgstr ""
@@ -1214,6 +1238,9 @@ msgstr ""
msgid "{receivedTokenSymbolText} Token"
msgstr ""
+msgid "Ruleset #1 starts when you create your project. With unlocked rulesets, you can edit your project's rules at any time. This gives you more flexibility, but may appear risky to supporters. Switching to locked rulesets will help you build supporter confidence."
+msgstr ""
+
msgid "Issuance reduction rate:"
msgstr ""
@@ -1286,6 +1313,9 @@ msgstr ""
msgid "Migrate payment terminal"
msgstr ""
+msgid "Failed to create ERC20 token: {0}"
+msgstr ""
+
msgid "Payments"
msgstr ""
@@ -1397,6 +1427,9 @@ msgstr ""
msgid "What do we value?"
msgstr ""
+msgid "Ruleset configuration"
+msgstr ""
+
msgid "Add a brief one-sentence summary of your project."
msgstr ""
@@ -1418,6 +1451,9 @@ msgstr ""
msgid "<0>Juicebox is a <1>governance-minimal1> protocol. There are only a few levers that can be tuned, none of which impose changes for users without their consent. The Juicebox governance smart contract can adjust these levers.0><2>The Juicebox protocol is governed by a community of JBX token holders who vote on proposals fortnightly.2><3>Juicebox is on-chain and non-custodial. Project creators actually own their projects, and JuiceboxDAO has no way to access project's ETH or change their rules.3>"
msgstr ""
+msgid "<0>With unlocked rulesets, you can edit your project's rules at any time.0><1>With locked rulesets, you can lock your project's rules for a period of time (like 3 minutes, 2 years, or 14 days), helping you build trust with your supporters.1><2>This choice isn't permanent — you can switch between locked and unlocked rulesets in the future.2>"
+msgstr ""
+
msgid "Pay {projectTitle}"
msgstr ""
@@ -1784,6 +1820,9 @@ msgstr ""
msgid "Created project"
msgstr ""
+msgid "Next ruleset, the project will issue {0} tokens per 1 ETH. The ruleset after that, the project will issue {1} tokens per 1 ETH."
+msgstr ""
+
msgid "An address is required"
msgstr ""
@@ -1802,6 +1841,9 @@ msgstr ""
msgid "Project rules"
msgstr ""
+msgid "Set a duration for locked rulesets."
+msgstr ""
+
msgid "Upload"
msgstr ""
@@ -2105,6 +2147,9 @@ msgstr ""
msgid "Reset website"
msgstr ""
+msgid "Decay percent"
+msgstr ""
+
msgid "Payout allocated to this project's {versionName} payment terminal. <0>Learn more0>."
msgstr ""
@@ -2420,6 +2465,9 @@ msgstr ""
msgid "While enabled, the project owner can change the project's <0>controller0> at any time."
msgstr ""
+msgid "<0>With Locked Rulesets, your project's rules are locked for a period of time.0><1><2>This helps build trust with your contributors.2>1>"
+msgstr ""
+
msgid "Ruleset #"
msgstr ""
@@ -2501,6 +2549,9 @@ msgstr ""
msgid "Disclose any details to your contributors before they pay your project."
msgstr ""
+msgid "The issuance rate is reduced by this percentage every ruleset (every <0>{0}0>). The higher this rate, the more incentive to pay this project earlier."
+msgstr ""
+
msgid "One or more reserved token recipients"
msgstr ""
@@ -2615,6 +2666,9 @@ msgstr ""
msgid "Your edits will take effect in <0>cycle #{0}0>. The current cycle (#{currentFCNumber}) won't be altered."
msgstr ""
+msgid "Leave this blank to start your first ruleset immediately after you finish setting up your project."
+msgstr ""
+
msgid "All of this project's ETH will be paid out. Token holders will receive <0>no ETH0> when redeeming their tokens."
msgstr ""
@@ -2642,6 +2696,9 @@ msgstr ""
msgid "Give permissions to {0} on project #{projectId}"
msgstr ""
+msgid "Pay out ETH from your project to any Ethereum wallet or Juicebox project. ETH which <0>isn't0> paid out will be available for token redemptions, or for use in future rulesets. Payouts reset each ruleset."
+msgstr ""
+
msgid "<0>Juicebox has had <1>multiple security audits1>, and has handled tens of thousands of ETH through its protocol.0><2>However, Juicebox is still experimental software. Although the Juicebox contract team have done their part to shape the smart contracts for public use and have tested the code thoroughly, the risk of exploits is never 0%.2><3>Due to their public nature, any exploits to the contracts may have irreversible consequences, including loss of ETH. Please use Juicebox with caution.3><4><5>Learn more about the risks.5>4>"
msgstr ""
@@ -2750,9 +2807,6 @@ msgstr ""
msgid "We've disabled payments because the project has opted to reserve 100% of new tokens. You would receive no tokens from your payment."
msgstr ""
-msgid "Get notifications"
-msgstr ""
-
msgid "Unarchiving your project has the following effects:"
msgstr ""
@@ -2855,6 +2909,9 @@ msgstr ""
msgid "Paying another Juicebox project may mint its tokens. Select an address to receive these tokens."
msgstr ""
+msgid "Set a future date & time to start your project's first ruleset."
+msgstr ""
+
msgid "Get help planning or setting up my project."
msgstr ""
@@ -3041,6 +3098,9 @@ msgstr ""
msgid "Unwatch"
msgstr ""
+msgid "V4"
+msgstr ""
+
msgid "Locked until <0>{value}0>"
msgstr ""
@@ -3071,6 +3131,9 @@ msgstr ""
msgid "Connect wallet to deploy"
msgstr ""
+msgid "Unlocked Rulesets"
+msgstr ""
+
msgid "Your wallet isn't allowed to process held fees."
msgstr ""
@@ -3086,6 +3149,9 @@ msgstr ""
msgid "Check User Wallet Address"
msgstr ""
+msgid "Rulesets"
+msgstr ""
+
msgid "Set ENS text record for {ensName}"
msgstr ""
@@ -3140,6 +3206,9 @@ msgstr ""
msgid "Yes, start over"
msgstr ""
+msgid "Ruleset #1 starts when you create your project. With locked rulesets, if you edit your project's rules during Ruleset #1, those edits will be <0>queued0> for the next ruleset."
+msgstr ""
+
msgid "All {tokensText} will go to the project owner:"
msgstr ""
@@ -3314,6 +3383,9 @@ msgstr ""
msgid "{0} is not a valid integer"
msgstr ""
+msgid "Rulesets & Payouts"
+msgstr ""
+
msgid "The unallocated portion of your total will go to the wallet that owns the project by default."
msgstr ""
@@ -3431,6 +3503,9 @@ msgstr ""
msgid "No changes"
msgstr ""
+msgid "After {0} (your first ruleset), your project will not issue any tokens unless you edit the issuance rate."
+msgstr ""
+
msgid "Project ENS name"
msgstr ""
@@ -3446,6 +3521,9 @@ msgstr ""
msgid "After {0} (your first cycle), your project will not issue any tokens unless you edit the issuance rate."
msgstr ""
+msgid "In other words: instead of taking effect immediately, those edits will take effect when the next ruleset starts (Ruleset #2). If you need more flexibility, switch to unlocked rulesets."
+msgstr ""
+
msgid "New NFTs will available on your project page shortly."
msgstr ""
@@ -3560,6 +3638,9 @@ msgstr ""
msgid "Redeem {tokensLabel} for ETH"
msgstr ""
+msgid "Locked Rulesets"
+msgstr ""
+
msgid "Made a mistake?"
msgstr ""
@@ -3737,6 +3818,9 @@ msgstr ""
msgid "DeFi"
msgstr ""
+msgid "Make changes to your ruleset settings and rules"
+msgstr ""
+
msgid "While enabled, this project will use the custom behavior defined in the contract above when somebody redeems from this project. Exercise caution."
msgstr ""
@@ -4223,6 +4307,9 @@ msgstr ""
msgid "The maximum supply of this NFT in circulation."
msgstr ""
+msgid "Each ruleset, the project will issue {discountRate}% fewer tokens per ETH."
+msgstr ""
+
msgid "Payout and reserved token recipients cannot exceed 100%"
msgstr ""
@@ -4301,6 +4388,9 @@ msgstr ""
msgid "End"
msgstr ""
+msgid "The issuance reduction rate is disabled if you are using unlocked rulesets (because they have no duration)."
+msgstr ""
+
msgid "Message sent!"
msgstr ""
diff --git a/src/models/dbProject.ts b/src/models/dbProject.ts
index 5deb193a58..91a07158b4 100644
--- a/src/models/dbProject.ts
+++ b/src/models/dbProject.ts
@@ -4,8 +4,8 @@ import { Database } from 'types/database.types'
import { Project } from 'generated/graphql'
import { ProjectTagName } from './project-tags'
import { PV } from './pv'
-
-export type SGSBCompareKey = Extract
+type P = Project & { chainId: number }
+export type SGSBCompareKey = Extract
/**
* @param text Text to use for string search
@@ -36,6 +36,7 @@ export type DBProject = {
projectId: number
createdAt: number
pv: PV
+ chainId: number
handle: string | null
metadataUri: string | null
diff --git a/src/packages/v1/components/V1Project/TokensSection.tsx b/src/packages/v1/components/V1Project/TokensSection.tsx
index 8bdd9884ba..6a7ec6d507 100644
--- a/src/packages/v1/components/V1Project/TokensSection.tsx
+++ b/src/packages/v1/components/V1Project/TokensSection.tsx
@@ -3,7 +3,6 @@ import { Button, Descriptions, Space, Statistic } from 'antd'
import { IssueErc20TokenButton } from 'components/buttons/IssueErc20TokenButton'
import EthereumAddress from 'components/EthereumAddress'
import ManageTokensModal from 'components/modals/ManageTokensModal'
-import ParticipantsModal from 'components/modals/ParticipantsModal'
import SectionHeader from 'components/SectionHeader'
import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext'
import { BigNumber } from 'ethers'
@@ -19,6 +18,7 @@ import { useV1UnclaimedBalance } from 'packages/v1/hooks/contractReader/useV1Unc
import { useTransferTokensTx } from 'packages/v1/hooks/transactor/useTransferTokensTx'
import { V1OperatorPermission } from 'packages/v1/models/permissions'
import { decodeFundingCycleMetadata } from 'packages/v1/utils/fundingCycle'
+import ParticipantsModal from 'packages/v2v3/components/V2V3Project/modals/ParticipantsModal'
import { CSSProperties, useContext, useState } from 'react'
import { isZeroAddress } from 'utils/address'
import { formatPercent, formatWad } from 'utils/format/formatNumber'
diff --git a/src/packages/v2v3/components/Create/hooks/DeployProject/useDeployProject.ts b/src/packages/v2v3/components/Create/hooks/DeployProject/useDeployProject.ts
index cb09e58ba1..fa363ac619 100644
--- a/src/packages/v2v3/components/Create/hooks/DeployProject/useDeployProject.ts
+++ b/src/packages/v2v3/components/Create/hooks/DeployProject/useDeployProject.ts
@@ -56,7 +56,7 @@ const getProjectIdFromLaunchReceipt = (
* Attempt to find the transaction receipt from a transaction hash.
* Will retry up to 5 times with a 2 second delay between each attempt. If no
- * receipt is found after 5 attempts, undefined is returned.
+ * receipt is not found after 5 attempts, undefined is returned.
*
* @param txHash transaction hash
* @returns transaction receipt or undefined
diff --git a/src/packages/v2v3/components/Create/hooks/useLoadInitialStateFromQuery.ts b/src/packages/v2v3/components/Create/hooks/useLoadInitialStateFromQuery.ts
index ae6e947017..a2d3097086 100644
--- a/src/packages/v2v3/components/Create/hooks/useLoadInitialStateFromQuery.ts
+++ b/src/packages/v2v3/components/Create/hooks/useLoadInitialStateFromQuery.ts
@@ -1,10 +1,10 @@
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import isEqual from 'lodash/isEqual'
import { CreatePage } from 'models/createPage'
import { ProjectTokensSelection } from 'models/projectTokenSelection'
import { TreasurySelection } from 'models/treasurySelection'
import { useRouter } from 'next/router'
import { ballotStrategiesFn } from 'packages/v2v3/constants/ballotStrategies'
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
import { useDefaultJBETHPaymentTerminal } from 'packages/v2v3/hooks/defaultContracts/useDefaultJBETHPaymentTerminal'
import { MAX_DISTRIBUTION_LIMIT } from 'packages/v2v3/utils/math'
import { useEffect, useState } from 'react'
diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/HistorySubPanel.test.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/HistorySubPanel.test.tsx
deleted file mode 100644
index 49e3116fd3..0000000000
--- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/CyclesPayoutsPanel/components/HistorySubPanel.test.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * @jest-environment jsdom
- */
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import { render, screen } from '@testing-library/react'
-import { BigNumber } from 'ethers'
-import { FundingCyclesQuery } from 'generated/graphql'
-import { usePastFundingCycles } from '../hooks/usePastFundingCycles'
-import { HistorySubPanel } from './HistorySubPanel'
-
-jest.mock('../hooks/usePastFundingCycles')
-jest.mock('@headlessui/react', () => {
- return {
- __esModule: true,
- Disclosure: jest.requireActual('@headlessui/react').Disclosure,
- Transition: jest
- .fn()
- .mockImplementation(({ children, show }) => (
-
- )),
- }
-})
-
-describe('HistorySubPanel', () => {
- const mockFundingCycles: FundingCyclesQuery['fundingCycles'] = [
- {
- ballot: '0x4b9f876c7fc5f6def8991fde639b2c812a85fb12',
- ballotRedemptionRate: 6000,
- basedOn: 1685615915,
- burnPaused: false,
- configuration: BigNumber.from('1686266495'),
- controllerMigrationAllowed: true,
- dataSource: '0x0000000000000000000000000000000000000000',
- discountRate: BigNumber.from('15000000'),
- distributionsPaused: false,
- duration: 604800,
- id: '2-397-37',
- metadata: BigNumber.from('453635417129768049443073'),
- metametadata: 0,
- mintingAllowed: false,
- mustStartAtOrAfter: null,
- number: 37,
- pausePay: false,
- preferClaimedTokenOverride: false,
- projectId: 397,
- redeemPaused: false,
- redemptionRate: 6000,
- reservedRate: 5000,
- setControllerAllowed: false,
- setTerminalsAllowed: true,
- shouldHoldFees: false,
- startTimestamp: 1694997023,
- terminalMigrationAllowed: true,
- transfersPaused: false,
- useDataSourceForPay: false,
- useDataSourceForRedeem: false,
- useTotalOverflowForRedemptions: false,
- weight: BigNumber.from('341957057837004498728584'),
- withdrawnAmount: BigNumber.from('30779487181046138000000'),
- },
- ]
-
- beforeEach(() => {
- ;(usePastFundingCycles as jest.Mock).mockReturnValue({
- loading: false,
- data: {
- fundingCycles: mockFundingCycles,
- },
- error: null,
- })
- })
-
- it('renders without crashing', () => {
- render()
- })
-
- it('displays correct cycle data', () => {
- render()
- expect(
- screen.getByText(`#${mockFundingCycles[0].number}`),
- ).toBeInTheDocument()
- })
-})
diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokenHoldersModal/TokenHoldersModal.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokenHoldersModal/TokenHoldersModal.tsx
index 2ee4b5018c..8194368674 100644
--- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokenHoldersModal/TokenHoldersModal.tsx
+++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/TokenHoldersModal/TokenHoldersModal.tsx
@@ -1,4 +1,4 @@
-import ParticipantsModal from 'components/modals/ParticipantsModal'
+import ParticipantsModal from 'packages/v2v3/components/V2V3Project/modals/ParticipantsModal'
import { useProjectContext } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectContext'
// TODO: This is hacked together - we should consider rebuilding
diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts
index e5a7cb07bc..d2a9075db0 100644
--- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts
+++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/hooks/useInitialEditingData.ts
@@ -1,9 +1,9 @@
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import {
ETH_PAYOUT_SPLIT_GROUP,
RESERVED_TOKEN_SPLIT_GROUP,
} from 'constants/splits'
import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext'
-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'
import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext'
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..4153ce30b5 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,5 +1,5 @@
import { BigNumber } from '@ethersproject/bignumber'
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import { V2V3ProjectContractsContext } from 'packages/v2v3/contexts/ProjectContracts/V2V3ProjectContractsContext'
import {
V2V3FundAccessConstraint,
diff --git a/src/components/modals/DownloadParticipantsModal.tsx b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/DownloadParticipantsModal.tsx
similarity index 100%
rename from src/components/modals/DownloadParticipantsModal.tsx
rename to src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/DownloadParticipantsModal.tsx
diff --git a/src/components/modals/ParticipantsModal/HoldersList.tsx b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/HoldersList.tsx
similarity index 98%
rename from src/components/modals/ParticipantsModal/HoldersList.tsx
rename to src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/HoldersList.tsx
index 104ae4a9e4..34b5424d81 100644
--- a/src/components/modals/ParticipantsModal/HoldersList.tsx
+++ b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/HoldersList.tsx
@@ -21,7 +21,7 @@ import { PV } from 'models/pv'
import { useState } from 'react'
import { formatPercent } from 'utils/format/formatNumber'
import { tokenSymbolText } from 'utils/tokenSymbolText'
-import { DownloadParticipantsModal } from '../DownloadParticipantsModal'
+import { DownloadParticipantsModal } from './DownloadParticipantsModal'
interface ParticipantOption {
label: string
diff --git a/src/components/TokenDistributionChart/TokenAreaChart.tsx b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/TokenDistributionChart/TokenAreaChart.tsx
similarity index 100%
rename from src/components/TokenDistributionChart/TokenAreaChart.tsx
rename to src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/TokenDistributionChart/TokenAreaChart.tsx
diff --git a/src/components/TokenDistributionChart/TokenPieChart.tsx b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/TokenDistributionChart/TokenPieChart.tsx
similarity index 100%
rename from src/components/TokenDistributionChart/TokenPieChart.tsx
rename to src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/TokenDistributionChart/TokenPieChart.tsx
diff --git a/src/components/TokenDistributionChart/index.tsx b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/TokenDistributionChart/index.tsx
similarity index 100%
rename from src/components/TokenDistributionChart/index.tsx
rename to src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/TokenDistributionChart/index.tsx
diff --git a/src/components/modals/ParticipantsModal/index.tsx b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/index.tsx
similarity index 87%
rename from src/components/modals/ParticipantsModal/index.tsx
rename to src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/index.tsx
index 6d8d03aeb9..99db1d5b18 100644
--- a/src/components/modals/ParticipantsModal/index.tsx
+++ b/src/packages/v2v3/components/V2V3Project/modals/ParticipantsModal/index.tsx
@@ -9,16 +9,16 @@ import { isZeroAddress } from 'utils/address'
import { tokenSymbolText } from 'utils/tokenSymbolText'
import { useQuery } from '@tanstack/react-query'
-import TokenDistributionChart from 'components/TokenDistributionChart'
import {
OrderDirection,
- Participant_OrderBy,
- ParticipantsDocument,
ParticipantsQuery,
QueryParticipantsArgs,
+ Participant_OrderBy as V1V2V3Participant_OrderBy,
+ ParticipantsDocument as V1V2V3ParticipantsDocument,
} from 'generated/graphql'
import { client } from 'lib/apollo/client'
import { paginateDepleteQuery } from 'lib/apollo/paginateDepleteQuery'
+import TokenDistributionChart from 'packages/v2v3/components/V2V3Project/modals/ParticipantsModal/TokenDistributionChart'
import HoldersList from './HoldersList'
export default function ParticipantsModal({
@@ -41,20 +41,19 @@ export default function ParticipantsModal({
queryFn: () =>
paginateDepleteQuery({
client,
- document: ParticipantsDocument,
+ document: V1V2V3ParticipantsDocument,
variables: {
orderDirection: OrderDirection.desc,
- orderBy: Participant_OrderBy.balance,
+ orderBy: V1V2V3Participant_OrderBy.balance,
where: {
projectId,
pv,
- balance_gt: BigNumber.from(0),
wallet_not: constants.AddressZero,
},
- },
+ }
}),
staleTime: 5 * 60 * 1000, // 5 min
- enabled: Boolean(projectId && pv && open),
+ enabled: Boolean(projectId && open),
})
return (
diff --git a/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts b/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts
index c28f7ef2e0..cbbeba406d 100644
--- a/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts
+++ b/src/packages/v2v3/hooks/JB721Delegate/transactor/useLaunchProjectWithNftsTx.ts
@@ -20,7 +20,7 @@ import { useJBPrices } from 'packages/v2v3/hooks/JBPrices'
import { DEFAULT_JB_721_DELEGATE_VERSION } from 'packages/v2v3/hooks/defaultContracts/useDefaultJB721Delegate'
import { useDefaultJBController } from 'packages/v2v3/hooks/defaultContracts/useDefaultJBController'
import { useDefaultJBETHPaymentTerminal } from 'packages/v2v3/hooks/defaultContracts/useDefaultJBETHPaymentTerminal'
-import { LaunchProjectData } from 'packages/v2v3/hooks/transactor/useLaunchProjectTx'
+import { LaunchV2V3ProjectData } from 'packages/v2v3/hooks/transactor/useLaunchProjectTx'
import { useV2ProjectTitle } from 'packages/v2v3/hooks/useProjectTitle'
import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption'
import {
@@ -61,7 +61,7 @@ interface JB721DelegateLaunchFundingCycleData {
interface LaunchProjectWithNftsTxArgs {
tiered721DelegateData: DeployTiered721DelegateData
- projectData: LaunchProjectData
+ projectData: LaunchV2V3ProjectData
}
type JB721DelegateLaunchProjectData = JB721DelegateLaunchFundingCycleData & {
diff --git a/src/packages/v2v3/hooks/contractReader/useProjectDistributionLimit.ts b/src/packages/v2v3/hooks/contractReader/useProjectDistributionLimit.ts
index 8654993b24..2bba1d8a25 100644
--- a/src/packages/v2v3/hooks/contractReader/useProjectDistributionLimit.ts
+++ b/src/packages/v2v3/hooks/contractReader/useProjectDistributionLimit.ts
@@ -1,5 +1,5 @@
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import { BigNumber } from 'ethers'
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
import { V2V3ProjectContractsContext } from 'packages/v2v3/contexts/ProjectContracts/V2V3ProjectContractsContext'
import { V2V3ContractName } from 'packages/v2v3/models/contracts'
import { useContext } from 'react'
diff --git a/src/packages/v2v3/hooks/contractReader/useProjectPrimaryEthTerminalAddress.ts b/src/packages/v2v3/hooks/contractReader/useProjectPrimaryEthTerminalAddress.ts
index 000b2d67e9..93f34c7b29 100644
--- a/src/packages/v2v3/hooks/contractReader/useProjectPrimaryEthTerminalAddress.ts
+++ b/src/packages/v2v3/hooks/contractReader/useProjectPrimaryEthTerminalAddress.ts
@@ -1,4 +1,4 @@
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import { V2V3ContractName } from 'packages/v2v3/models/contracts'
import useV2ContractReader from './useV2ContractReader'
diff --git a/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3.ts b/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3.ts
index 9fd936041a..7b597cf2ca 100644
--- a/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3.ts
+++ b/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3.ts
@@ -1,6 +1,6 @@
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import { DEFAULT_MEMO, DEFAULT_METADATA } from 'constants/transactionDefaults'
import { BigNumber } from 'ethers'
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
export function getAddToBalanceArgsV3({
projectId,
diff --git a/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3_1.ts b/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3_1.ts
index 06f2c558c8..bf643fad7f 100644
--- a/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3_1.ts
+++ b/src/packages/v2v3/hooks/transactor/AddToBalanceTx/useAddToBalanceArgsV3_1.ts
@@ -1,6 +1,6 @@
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import { DEFAULT_MEMO, DEFAULT_METADATA } from 'constants/transactionDefaults'
import { BigNumber } from 'ethers'
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
export function getAddToBalanceArgsV3_1({
projectId,
diff --git a/src/packages/v2v3/hooks/transactor/useDistributePayouts.ts b/src/packages/v2v3/hooks/transactor/useDistributePayouts.ts
index 1c299b93e1..250fac7d28 100644
--- a/src/packages/v2v3/hooks/transactor/useDistributePayouts.ts
+++ b/src/packages/v2v3/hooks/transactor/useDistributePayouts.ts
@@ -1,4 +1,5 @@
import { t } from '@lingui/macro'
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import {
DEFAULT_MEMO,
DEFAULT_METADATA,
@@ -8,7 +9,6 @@ import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext'
import { TransactionContext } from 'contexts/Transaction/TransactionContext'
import { BigNumber } from 'ethers'
import { TransactorInstance } from 'hooks/useTransactor'
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
import { V2V3ProjectContractsContext } from 'packages/v2v3/contexts/ProjectContracts/V2V3ProjectContractsContext'
import {
PaymentTerminalVersion,
diff --git a/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts b/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts
index 4a427fc026..83384ddcf6 100644
--- a/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts
+++ b/src/packages/v2v3/hooks/transactor/useLaunchProjectTx.ts
@@ -21,7 +21,7 @@ import { useContext } from 'react'
import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project'
import { useV2ProjectTitle } from '../useProjectTitle'
-export interface LaunchProjectData {
+export interface LaunchV2V3ProjectData {
projectMetadataCID: string
fundingCycleData: V2V3FundingCycleData
fundingCycleMetadata: V2V3FundingCycleMetadata
@@ -31,7 +31,7 @@ export interface LaunchProjectData {
owner?: string // If not provided, the current user's address will be used.
}
-export function useLaunchProjectTx(): TransactorInstance {
+export function useLaunchProjectTx(): TransactorInstance {
const { transactor } = useContext(TransactionContext)
const { contracts } = useContext(V2V3ContractsContext)
const defaultJBController = useDefaultJBController()
diff --git a/src/packages/v2v3/hooks/transactor/usePayETHPaymentTerminalTx.ts b/src/packages/v2v3/hooks/transactor/usePayETHPaymentTerminalTx.ts
index e31c3b08b7..94ac58f052 100644
--- a/src/packages/v2v3/hooks/transactor/usePayETHPaymentTerminalTx.ts
+++ b/src/packages/v2v3/hooks/transactor/usePayETHPaymentTerminalTx.ts
@@ -1,11 +1,11 @@
import { t } from '@lingui/macro'
+import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import { DEFAULT_MIN_RETURNED_TOKENS } from 'constants/transactionDefaults'
import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext'
import { TransactionContext } from 'contexts/Transaction/TransactionContext'
import { BigNumber } from 'ethers'
import { TransactorInstance } from 'hooks/useTransactor'
import { useProjectIsOFACListed } from 'packages/v2v3/components/V2V3Project/ProjectDashboard/hooks/useProjectIsOFACListed'
-import { ETH_TOKEN_ADDRESS } from 'packages/v2v3/constants/juiceboxTokens'
import { V2V3ProjectContractsContext } from 'packages/v2v3/contexts/ProjectContracts/V2V3ProjectContractsContext'
import { useContext } from 'react'
import { useV2V3BlockedProject } from '../useBlockedProject'
diff --git a/src/packages/v2v3/hooks/transactor/useReconfigureV2V3FundingCycleTx.ts b/src/packages/v2v3/hooks/transactor/useReconfigureV2V3FundingCycleTx.ts
index 53f1cec29c..ca4b0c94ac 100644
--- a/src/packages/v2v3/hooks/transactor/useReconfigureV2V3FundingCycleTx.ts
+++ b/src/packages/v2v3/hooks/transactor/useReconfigureV2V3FundingCycleTx.ts
@@ -8,10 +8,10 @@ import { isValidMustStartAtOrAfter } from 'packages/v2v3/utils/fundingCycle'
import { useContext } from 'react'
import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project'
import { useV2ProjectTitle } from '../useProjectTitle'
-import { LaunchProjectData } from './useLaunchProjectTx'
+import { LaunchV2V3ProjectData } from './useLaunchProjectTx'
export type ReconfigureFundingCycleTxParams = Omit<
- LaunchProjectData,
+ LaunchV2V3ProjectData,
'projectMetadataCID'
> & {
memo?: string
diff --git a/src/packages/v4/components/Create/Create.tsx b/src/packages/v4/components/Create/Create.tsx
new file mode 100644
index 0000000000..e641cd834c
--- /dev/null
+++ b/src/packages/v4/components/Create/Create.tsx
@@ -0,0 +1,126 @@
+import { t, Trans } from '@lingui/macro'
+import { Badge } from 'components/Badge'
+import { DeployButtonText } from 'components/buttons/DeployProjectButtonText'
+import Loading from 'components/Loading'
+import {
+ RECONFIG_RULES_EXPLANATION,
+ RULESET_EXPLANATION,
+} from 'components/strings'
+import { readNetwork } from 'constants/networks'
+import { NetworkName } from 'models/networkName'
+import { useRouter } from 'next/router'
+import { FundingCyclesPage } from './components/pages/FundingCycles/FundingCyclesPage'
+import { PayoutsPage } from './components/pages/PayoutsPage/PayoutsPage'
+import { ProjectDetailsPage } from './components/pages/ProjectDetails/ProjectDetailsPage'
+import { ProjectTokenPage } from './components/pages/ProjectToken/ProjectTokenPage'
+import { ReconfigurationRulesPage } from './components/pages/ReconfigurationRules/ReconfigurationRulesPage'
+import { DeploySuccess } from './components/pages/ReviewDeploy/components/DeploySuccess'
+import { ReviewDeployPage } from './components/pages/ReviewDeploy/ReviewDeployPage'
+import { Wizard } from './components/Wizard/Wizard'
+import { useLoadingInitialStateFromQuery } from './hooks/useLoadInitialStateFromQuery'
+
+export function Create() {
+ const router = useRouter()
+ const deployedProjectId = router.query.deployedProjectId as string
+ const initialStateLoading = useLoadingInitialStateFromQuery()
+
+ if (initialStateLoading) return
+
+ if (deployedProjectId) {
+ const projectId = parseInt(deployedProjectId)
+ return
+ }
+
+ return (
+
+
+
+ Create a project
+ Beta
+
+
+ {/* TODO: Remove wizard-create once form item css override is replaced */}
+
+ }>
+
+
+
+
+
+
+
+ Pay out ETH from your project to any Ethereum wallet or Juicebox
+ project. ETH which isn't paid out will be available for
+ token redemptions, or for use in future rulesets. Payouts reset
+ each ruleset.
+
+ }
+ >
+
+
+
+ When people pay your project, they receive its tokens. Project
+ tokens can be used for governance or community access, and token
+ holders can redeem their tokens to reclaim some ETH from your
+ project. You can also reserve some tokens for recipients of your
+ choosing.
+
+ }
+ >
+
+
+ {/*
+ NFTs
+
+
+ }
+ description={
+ Reward your supporters with custom NFTs.
+ }
+ >
+
+ */}
+ Edit Deadline}
+ description={RECONFIG_RULES_EXPLANATION}
+ >
+
+
+
+ Review your project and deploy it to{' '}
+ {readNetwork.name ?? NetworkName.mainnet}.
+
+ }
+ >
+
+
+
+