diff --git a/package.json b/package.json
index a3b93e6e65..c9b9f8708e 100644
--- a/package.json
+++ b/package.json
@@ -107,8 +107,8 @@
"graphql": "^16.8.1",
"he": "^1.2.0",
"jsonwebtoken": "^9.0.0",
- "juice-sdk-core": "^11.0.0-alpha",
- "juice-sdk-react": "^11.0.0-alpha",
+ "juice-sdk-core": "^11.5.0-alpha",
+ "juice-sdk-react": "^11.6.0-alpha",
"juicebox-metadata-helper": "0.1.7",
"less": "4.1.2",
"lodash": "^4.17.21",
diff --git a/src/packages/v4/components/Create/Create.tsx b/src/packages/v4/components/Create/Create.tsx
index e4bd960031..12d0fe009e 100644
--- a/src/packages/v4/components/Create/Create.tsx
+++ b/src/packages/v4/components/Create/Create.tsx
@@ -10,7 +10,9 @@ import Loading from 'components/Loading'
import { readNetwork } from 'constants/networks'
import { NetworkName } from 'models/networkName'
import { useRouter } from 'next/router'
+import { CreateBadge } from './components/CreateBadge'
import { FundingCyclesPage } from './components/pages/FundingCycles/FundingCyclesPage'
+import { NftRewardsPage } from './components/pages/NftRewards/NftRewardsPage'
import { PayoutsPage } from './components/pages/PayoutsPage/PayoutsPage'
import { ProjectDetailsPage } from './components/pages/ProjectDetails/ProjectDetailsPage'
import { ProjectTokenPage } from './components/pages/ProjectToken/ProjectTokenPage'
@@ -86,7 +88,7 @@ export function Create() {
>
- {/*
@@ -99,7 +101,7 @@ export function Create() {
}
>
- */}
+
Edit Deadline}
diff --git a/src/packages/v4/components/Create/components/Wizard/hooks/useSteps.ts b/src/packages/v4/components/Create/components/Wizard/hooks/useSteps.ts
index 2c58a52f47..89862b1bdc 100644
--- a/src/packages/v4/components/Create/components/Wizard/hooks/useSteps.ts
+++ b/src/packages/v4/components/Create/components/Wizard/hooks/useSteps.ts
@@ -1,6 +1,7 @@
+import { useCallback, useContext, useMemo } from 'react'
+
import { t } from '@lingui/macro'
import { CreatePage } from 'models/createPage'
-import { useCallback, useContext, useMemo } from 'react'
import { useAppSelector } from 'redux/hooks/useAppSelector'
import { useEditingCreateFurthestPageReached } from 'redux/hooks/useEditingCreateFurthestPageReached'
import { WizardContext } from '../contexts/WizardContext'
@@ -11,7 +12,7 @@ const stepNames = (): Record => {
fundingCycles: t`Rulesets`,
payouts: t`Payouts`,
projectToken: t`Token`,
- // nftRewards: t`NFTs`,
+ nftRewards: t`NFTs`,
reconfigurationRules: t`Deadline`,
reviewDeploy: t`Deploy`,
}
diff --git a/src/packages/v4/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx b/src/packages/v4/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx
index 17d5d5554e..7302f3591b 100644
--- a/src/packages/v4/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx
+++ b/src/packages/v4/components/Create/components/pages/ReviewDeploy/ReviewDeployPage.tsx
@@ -24,6 +24,7 @@ import { WizardContext } from '../../Wizard/contexts/WizardContext'
import { FundingConfigurationReview } from './components/FundingConfigurationReview/FundingConfigurationReview'
import { ProjectDetailsReview } from './components/ProjectDetailsReview/ProjectDetailsReview'
import { ProjectTokenReview } from './components/ProjectTokenReview/ProjectTokenReview'
+import { RewardsReview } from './components/RewardsReview/RewardsReview'
import { RulesReview } from './components/RulesReview/RulesReview'
enum ReviewDeployKey {
@@ -164,7 +165,7 @@ export const ReviewDeployPage = () => {
>
- {/* {
}
>
- */}
+
{
+ const rewardTier = sortedRewardTiers[index]
+
+ return nftRewardTierToJB721TierConfig(rewardTier, cid)
+ })
+}
+
+function toV4Flags(v2v3Flags: JBTiered721Flags): JB721TiersHookFlags {
+ return {
+ noNewTiersWithOwnerMinting: v2v3Flags.lockManualMintingChanges,
+ noNewTiersWithReserves: v2v3Flags.lockReservedTokenChanges,
+ noNewTiersWithVotes: v2v3Flags.lockVotingUnitChanges,
+ preventOverspending: v2v3Flags.preventOverspending,
+ }
+}
/**
* Hook that returns a function that deploys a project with NFT rewards.
@@ -33,22 +100,12 @@ export const useDeployNftProject = () => {
const fundingCycleData = useEditingV2V3FundingCycleDataSelector()
const fundAccessConstraints = useEditingV2V3FundAccessConstraintsSelector()
- const collectionName = useMemo(
- () =>
- nftRewards.collectionMetadata.name
- ? nftRewards.collectionMetadata.name
- : projectMetadata.name,
- [nftRewards.collectionMetadata.name, projectMetadata.name],
- )
- const collectionSymbol = useMemo(
- () => nftRewards.collectionMetadata.symbol ?? '',
- [nftRewards.collectionMetadata.symbol],
- )
- const nftFlags = useMemo(
- () => nftRewards.flags ?? DEFAULT_NFT_FLAGS,
- [nftRewards.flags],
- )
- const governanceType = nftRewards.governanceType
+ const collectionName = nftRewards.collectionMetadata.name
+ ? nftRewards.collectionMetadata.name
+ : projectMetadata.name
+ const collectionSymbol = nftRewards.collectionMetadata.symbol ?? ''
+ const nftFlags = nftRewards.flags ?? DEFAULT_NFT_FLAGS
+ // const governanceType = nftRewards.governanceType
const currency = nftRewards.pricing.currency
/**
@@ -63,15 +120,14 @@ export const useDeployNftProject = () => {
rewardTierCids,
nftCollectionMetadataUri,
- onDone,
- onConfirmed,
- onCancelled,
- onError,
+ onTransactionPending,
+ onTransactionConfirmed,
+ onTransactionError,
}: {
metadataCid: string
rewardTierCids: string[]
nftCollectionMetadataUri: string
- } & TransactionCallbacks) => {
+ } & LaunchTxOpts) => {
if (!collectionName) throw new Error('No collection name or project name')
if (!(rewardTierCids.length && nftRewards.rewardTiers))
throw new Error('No NFTs')
@@ -80,8 +136,8 @@ export const useDeployNftProject = () => {
const tiers = buildJB721TierParams({
cids: rewardTierCids,
rewardTiers: nftRewards.rewardTiers,
- version: DEFAULT_JB_721_DELEGATE_VERSION,
})
+ const flags = toV4Flags(nftFlags)
return await launchProjectWithNftsTx(
{
@@ -90,9 +146,8 @@ export const useDeployNftProject = () => {
collectionName,
collectionSymbol,
currency,
- governanceType,
tiers,
- flags: nftFlags,
+ flags,
},
projectData: {
owner: inputProjectOwner?.length ? inputProjectOwner : undefined,
@@ -108,10 +163,9 @@ export const useDeployNftProject = () => {
},
},
{
- onDone,
- onConfirmed,
- onCancelled,
- onError,
+ onTransactionPending,
+ onTransactionConfirmed,
+ onTransactionError,
},
)
},
@@ -123,7 +177,6 @@ export const useDeployNftProject = () => {
reservedTokensGroupedSplits,
launchProjectWithNftsTx,
collectionSymbol,
- governanceType,
inputProjectOwner,
fundingCycleData,
mustStartAtOrAfter,
diff --git a/src/packages/v4/components/Create/hooks/DeployProject/useDeployProject.ts b/src/packages/v4/components/Create/hooks/DeployProject/useDeployProject.ts
index a0de85a48a..843ea6aeb2 100644
--- a/src/packages/v4/components/Create/hooks/DeployProject/useDeployProject.ts
+++ b/src/packages/v4/components/Create/hooks/DeployProject/useDeployProject.ts
@@ -1,15 +1,19 @@
-import { uploadProjectMetadata } from 'lib/api/ipfs'
-import { LaunchTxOpts } from 'packages/v4/hooks/useLaunchProjectTx'
import { useCallback, useState } from 'react'
-import { useAppDispatch } from 'redux/hooks/useAppDispatch'
import {
useAppSelector,
useEditingV2V3FundAccessConstraintsSelector,
useEditingV2V3FundingCycleDataSelector,
useEditingV2V3FundingCycleMetadataSelector,
} from 'redux/hooks/useAppSelector'
+
+import { uploadProjectMetadata } from 'lib/api/ipfs'
+import { LaunchTxOpts } from 'packages/v4/hooks/useLaunchProjectTx'
+import { useAppDispatch } from 'redux/hooks/useAppDispatch'
import { editingV2ProjectActions } from 'redux/slices/editingV2Project'
import { emitErrorNotification } from 'utils/notifications'
+import { useDeployNftProject } from './hooks/NFT/useDeployNftProject'
+import { useIsNftProject } from './hooks/NFT/useIsNftProject'
+import { useUploadNftRewards } from './hooks/NFT/useUploadNftRewards'
import { useDeployStandardProject } from './hooks/useDeployStandardProject'
const JUICEBOX_DOMAIN = 'juicebox'
@@ -22,10 +26,9 @@ export const useDeployProject = () => {
const [isDeploying, setIsDeploying] = useState(false)
const [transactionPending, setTransactionPending] = useState()
- // const isNftProject = useIsNftProject()
- // const uploadNftRewards = useUploadNftRewards()
- // const deployNftProject = useDeployNftProject()
-
+ const isNftProject = useIsNftProject()
+ const uploadNftRewards = useUploadNftRewards()
+ const deployNftProject = useDeployNftProject()
const deployStandardProject = useDeployStandardProject()
const {
@@ -89,15 +92,15 @@ export const useDeployProject = () => {
setIsDeploying(false)
throw new Error('Error deploying project.')
}
- // let nftCids: Awaited> | undefined
- // try {
- // if (isNftProject) {
- // nftCids = await uploadNftRewards()
- // }
- // } catch (error) {
- // handleDeployFailure(error)
- // return
- // }
+ let nftCids: Awaited> | undefined
+ try {
+ if (isNftProject) {
+ nftCids = await uploadNftRewards()
+ }
+ } catch (error) {
+ handleDeployFailure(error)
+ return
+ }
let softTargetAmount: string | undefined
let softTargetCurrency: string | undefined
@@ -120,42 +123,42 @@ export const useDeployProject = () => {
}
try {
- // let tx
- // if (isNftProject) {
- // tx = await deployNftProject({
- // metadataCid: projectMetadataCid,
- // rewardTierCids: nftCids!.rewardTiers,
- // nftCollectionMetadataUri: nftCids!.nfCollectionMetadata,
- // ...operationCallbacks(onProjectDeployed),
- // })
- // } else {
- const tx = await deployStandardProject({
- metadataCid: projectMetadataCid,
- ...operationCallbacks(onProjectDeployed),
- })
- // }
- // if (!tx) {
- setIsDeploying(false)
- setTransactionPending(false)
+ let tx
+ if (isNftProject) {
+ tx = await deployNftProject({
+ metadataCid: projectMetadataCid,
+ rewardTierCids: nftCids!.rewardTiers,
+ nftCollectionMetadataUri: nftCids!.nfCollectionMetadata,
+ ...operationCallbacks(onProjectDeployed),
+ })
+ } else {
+ tx = await deployStandardProject({
+ metadataCid: projectMetadataCid,
+ ...operationCallbacks(onProjectDeployed),
+ })
+ }
+ if (!tx) {
+ setIsDeploying(false)
+ setTransactionPending(false)
return
- // }
+ }
} catch (error) {
handleDeployFailure(error)
return
}
},
[
- // deployNftProject,
+ deployNftProject,
deployStandardProject,
fundAccessConstraints,
fundingCycleData,
fundingCycleMetadata,
handleDeployFailure,
- // isNftProject,
+ isNftProject,
operationCallbacks,
postPayModal,
projectMetadata,
- // uploadNftRewards,
+ uploadNftRewards,
],
)
return {
diff --git a/src/packages/v4/contexts/V4ProjectMetadataProvider.tsx b/src/packages/v4/contexts/V4ProjectMetadataProvider.tsx
index 9841567941..d92dc5bbfd 100644
--- a/src/packages/v4/contexts/V4ProjectMetadataProvider.tsx
+++ b/src/packages/v4/contexts/V4ProjectMetadataProvider.tsx
@@ -1,6 +1,7 @@
+import { useJBProjectMetadataContext } from 'juice-sdk-react'
+
import { PV_V4 } from 'constants/pv'
import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext'
-import { useJBProjectMetadataContext } from 'juice-sdk-react'
import { PropsWithChildren } from 'react'
export default function V4ProjectMetadataProvider({
diff --git a/src/packages/v4/hooks/useCurrentRouteChainId.ts b/src/packages/v4/hooks/useCurrentRouteChainId.ts
index 58db1b737a..d5766f5bf1 100644
--- a/src/packages/v4/hooks/useCurrentRouteChainId.ts
+++ b/src/packages/v4/hooks/useCurrentRouteChainId.ts
@@ -5,7 +5,7 @@ export function useCurrentRouteChainId() {
const router = useRouter()
const { chainName } = router.query
if (!chainName) {
- return null
+ return undefined
}
return chainNameMap[chainName as string]
diff --git a/src/packages/v4/hooks/useLaunchProjectTx.ts b/src/packages/v4/hooks/useLaunchProjectTx.ts
index a337c808aa..cf1d758f64 100644
--- a/src/packages/v4/hooks/useLaunchProjectTx.ts
+++ b/src/packages/v4/hooks/useLaunchProjectTx.ts
@@ -1,3 +1,10 @@
+import { useCallback, useContext } from 'react'
+import { Address, WaitForTransactionReceiptReturnType } from 'viem'
+import {
+ LaunchV2V3ProjectArgs,
+ transformV2V3CreateArgsToV4,
+} from '../utils/launchProjectTransformers'
+
import { waitForTransactionReceipt } from '@wagmi/core'
import { JUICEBOX_MONEY_PROJECT_METADATA_DOMAIN } from 'constants/metadataDomain'
import { DEFAULT_MEMO } from 'constants/transactionDefaults'
@@ -6,14 +13,8 @@ import { useWallet } from 'hooks/Wallet'
import { NATIVE_TOKEN } from 'juice-sdk-core'
import { useWriteJbControllerLaunchProjectFor } from 'juice-sdk-react'
import { LaunchV2V3ProjectData } from 'packages/v2v3/hooks/transactor/useLaunchProjectTx'
-import { useCallback, useContext } from 'react'
import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project'
-import { Address, WaitForTransactionReceiptReturnType } from 'viem'
import { sepolia } from 'viem/chains'
-import {
- LaunchV2V3ProjectArgs,
- transformV2V3CreateArgsToV4,
-} from '../utils/launchProject'
import { wagmiConfig } from '../wagmiConfig'
import { useCurrentRouteChainId } from './useCurrentRouteChainId'
@@ -31,7 +32,7 @@ export interface LaunchTxOpts {
* Return the project ID created from a `launchProjectFor` transaction.
* @param txReceipt receipt of `launchProjectFor` transaction
*/
-const getProjectIdFromLaunchReceipt = (
+export const getProjectIdFromLaunchReceipt = (
txReceipt: WaitForTransactionReceiptReturnType,
): number => {
const projectIdHex: string | undefined =
@@ -46,14 +47,14 @@ const getProjectIdFromLaunchReceipt = (
* The contract addresses to use for deployment
* @todo not ideal to hardcode these addresses
*/
-const SUPPORTED_JB_MULTITERMINAL_ADDRESS = {
+export const SUPPORTED_JB_MULTITERMINAL_ADDRESS = {
'84532': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
'421614': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
'11155111': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
'11155420': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
}
-const SUPPORTED_JB_CONTROLLER_ADDRESS = {
+export const SUPPORTED_JB_CONTROLLER_ADDRESS = {
'84532': '0x219A5cE6d1c512D5b050ad2E3d380b8746BE0Cb8' as Address,
'421614': '0x219A5cE6d1c512D5b050ad2E3d380b8746BE0Cb8' as Address,
'11155111': '0x219A5cE6d1c512D5b050ad2E3d380b8746BE0Cb8' as Address,
diff --git a/src/packages/v4/hooks/useLaunchProjectWithNftsTx.ts b/src/packages/v4/hooks/useLaunchProjectWithNftsTx.ts
new file mode 100644
index 0000000000..ae2f584691
--- /dev/null
+++ b/src/packages/v4/hooks/useLaunchProjectWithNftsTx.ts
@@ -0,0 +1,234 @@
+import { waitForTransactionReceipt } from '@wagmi/core'
+import { JUICEBOX_MONEY_PROJECT_METADATA_DOMAIN } from 'constants/metadataDomain'
+import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext'
+import { useWallet } from 'hooks/Wallet'
+import { DEFAULT_MEMO, NATIVE_TOKEN, NATIVE_TOKEN_DECIMALS } from 'juice-sdk-core'
+import {
+ jbPricesAddress,
+ useJBContractContext,
+ useReadJb721TiersHookStoreTiersOf,
+ useWriteJb721TiersHookProjectDeployerLaunchProjectFor,
+} from 'juice-sdk-react'
+import { isValidMustStartAtOrAfter } from 'packages/v2v3/utils/fundingCycle'
+import {
+ JBDeploy721TiersHookConfig,
+ LaunchProjectWithNftsTxArgs,
+} from 'packages/v4/models/nfts'
+import { useContext } from 'react'
+import { DEFAULT_MUST_START_AT_OR_AFTER } from 'redux/slices/editingV2Project'
+import { ipfsUri } from 'utils/ipfs'
+import {
+ Address,
+ toBytes,
+ toHex,
+ WaitForTransactionReceiptReturnType,
+ zeroAddress,
+} from 'viem'
+import { sepolia } from 'viem/chains'
+import {
+ LaunchV2V3ProjectArgs,
+ transformV2V3CreateArgsToV4,
+} from '../utils/launchProjectTransformers'
+import { wagmiConfig } from '../wagmiConfig'
+import { useCurrentRouteChainId } from './useCurrentRouteChainId'
+import {
+ LaunchTxOpts,
+ SUPPORTED_JB_CONTROLLER_ADDRESS,
+ SUPPORTED_JB_MULTITERMINAL_ADDRESS
+} from './useLaunchProjectTx'
+
+function createSalt() {
+ const base: string = '0x' + Math.random().toString(16).slice(2) // idk lol
+ const salt = toHex(toBytes(base, { size: 32 }))
+
+ return salt
+}
+
+/**
+ * Return the project ID created from a `launchProjectFor` transaction.
+ * @param txReceipt receipt of `launchProjectFor` transaction
+ */
+export const getProjectIdFromNftLaunchReceipt = (
+ txReceipt: WaitForTransactionReceiptReturnType,
+): number => {
+ const projectIdHex: string | undefined =
+ txReceipt?.logs[0]?.topics?.[1]
+ if (!projectIdHex) return 0
+
+ const projectId = parseInt(projectIdHex, 16)
+ return projectId
+}
+
+/**
+ * The contract addresses to use for deployment
+ * @todo not ideal to hardcode these addresses
+ */
+export const SUPPORTED_JB_721_TIER_STORE = {
+ '84532': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
+ '421614': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
+ '11155111': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
+ '11155420': '0x4DeF0AA5B9CA095d11705284221b2878731ab4EF' as Address,
+}
+
+/**
+ *
+ * TODO still wip
+ */
+export function useLaunchProjectWithNftsTx() {
+ const { contracts } = useJBContractContext()
+ const { addTransaction } = useContext(TxHistoryContext)
+
+ const { userAddress } = useWallet()
+ const chainId = useCurrentRouteChainId() ?? sepolia.id // default to sepolia
+ const defaultJBController = chainId
+ ? SUPPORTED_JB_CONTROLLER_ADDRESS[chainId]
+ : undefined
+ const defaultJBETHPaymentTerminal = chainId
+ ? SUPPORTED_JB_MULTITERMINAL_ADDRESS[chainId]
+ : undefined
+ const JBTiered721DelegateStoreAddress = chainId
+ ? SUPPORTED_JB_721_TIER_STORE[chainId]
+ : undefined
+
+ const { writeContractAsync: writeLaunchProject } =
+ useWriteJb721TiersHookProjectDeployerLaunchProjectFor()
+
+ return async (
+ {
+ tiered721DelegateData: {
+ collectionUri,
+ collectionName,
+ collectionSymbol,
+ currency,
+ tiers,
+ flags,
+ },
+ projectData: {
+ projectMetadataCID,
+ fundingCycleData,
+ fundingCycleMetadata,
+ fundAccessConstraints,
+ groupedSplits = [],
+ mustStartAtOrAfter = DEFAULT_MUST_START_AT_OR_AFTER,
+ owner,
+ },
+ }: LaunchProjectWithNftsTxArgs,
+ {
+ onTransactionPending: onTransactionPendingCallback,
+ onTransactionConfirmed: onTransactionConfirmedCallback,
+ onTransactionError: onTransactionErrorCallback,
+ }: LaunchTxOpts,
+ ) => {
+ if (
+ !userAddress ||
+ !contracts ||
+ !defaultJBController ||
+ !defaultJBETHPaymentTerminal ||
+ !JBTiered721DelegateStoreAddress ||
+ !isValidMustStartAtOrAfter(mustStartAtOrAfter, fundingCycleData.duration)
+ ) {
+ const missingParam = !userAddress
+ ? 'userAddress'
+ : !contracts
+ ? 'contracts'
+ : !defaultJBController
+ ? 'defaultJBController'
+ : !JBTiered721DelegateStoreAddress
+ ? 'JBTiered721DelegateProjectDeployer'
+ : null
+
+ onTransactionErrorCallback?.(
+ new DOMException(
+ `Transaction failed, missing argument "${
+ missingParam ?? ''
+ }".`,
+ ),
+ )
+
+ return Promise.resolve(false)
+ }
+ const _owner = (owner?.length ? owner : userAddress) as Address
+
+ const deployTiered721HookData: JBDeploy721TiersHookConfig = {
+ name: collectionName,
+ symbol: collectionSymbol,
+ baseUri: ipfsUri(''),
+ tokenUriResolver: zeroAddress,
+ contractUri: ipfsUri(collectionUri),
+ tiersConfig: {
+ currency,
+ decimals: NATIVE_TOKEN_DECIMALS,
+ prices: jbPricesAddress[chainId],
+ tiers,
+ },
+ reserveBeneficiary: zeroAddress,
+ flags,
+ }
+
+ const v2v3LaunchProjectArgs = [
+ _owner,
+ [projectMetadataCID, JUICEBOX_MONEY_PROJECT_METADATA_DOMAIN],
+ fundingCycleData,
+ fundingCycleMetadata,
+ mustStartAtOrAfter,
+ groupedSplits,
+ fundAccessConstraints,
+ [defaultJBETHPaymentTerminal], // _terminals, just supporting single for now
+ // Eventually should be something like:
+ // getTerminalsFromFundAccessConstraints(
+ // fundAccessConstraints,
+ // contracts.primaryNativeTerminal.data,
+ // ),
+ DEFAULT_MEMO,
+ ] as LaunchV2V3ProjectArgs
+ const launchProjectData = transformV2V3CreateArgsToV4({
+ v2v3Args: v2v3LaunchProjectArgs,
+ primaryNativeTerminal: defaultJBETHPaymentTerminal,
+ currencyTokenAddress: NATIVE_TOKEN,
+ })
+
+ const args = [
+ _owner,
+ deployTiered721HookData, //_deployTiered721HookData
+ {
+ projectUri: launchProjectData[1],
+ rulesetConfigurations: launchProjectData[2],
+ terminalConfigurations: launchProjectData[3],
+ memo: launchProjectData[4],
+ }, // _launchProjectData,
+ defaultJBController,
+ // createSalt(),
+ ] as const
+
+ try {
+ // SIMULATE TX: TODO update for nfts
+ // const encodedData = encodeFunctionData({
+ // abi: jbControllerAbi, // ABI of the contract
+ // functionName: 'launchProjectFor',
+ // args,
+ // })
+
+ const hash = await writeLaunchProject({
+ chainId,
+ args,
+ })
+
+ type x = typeof useReadJb721TiersHookStoreTiersOf
+
+ onTransactionPendingCallback(hash)
+ addTransaction?.('Launch Project', { hash })
+ const transactionReceipt: WaitForTransactionReceiptReturnType =
+ await waitForTransactionReceipt(wagmiConfig, {
+ hash,
+ })
+
+ const newProjectId = getProjectIdFromNftLaunchReceipt(transactionReceipt)
+
+ onTransactionConfirmedCallback(hash, newProjectId)
+ } catch (e) {
+ onTransactionErrorCallback(
+ (e as Error) ?? new Error('Transaction failed'),
+ )
+ }
+ }
+}
diff --git a/src/packages/v4/models/fundAccessLimits.ts b/src/packages/v4/models/fundAccessLimits.ts
new file mode 100644
index 0000000000..d5e1f0b919
--- /dev/null
+++ b/src/packages/v4/models/fundAccessLimits.ts
@@ -0,0 +1,12 @@
+export type FundAccessLimitGroup = {
+ terminal: `0x${string}`;
+ token: `0x${string}`;
+ payoutLimits: {
+ amount: bigint;
+ currency: number;
+ }[];
+ surplusAllowances: {
+ amount: bigint;
+ currency: number;
+ }[];
+}
diff --git a/src/packages/v4/models/nfts.ts b/src/packages/v4/models/nfts.ts
new file mode 100644
index 0000000000..f0fcdd7ec1
--- /dev/null
+++ b/src/packages/v4/models/nfts.ts
@@ -0,0 +1,98 @@
+import { JBRulesetData, JBRulesetMetadata } from 'juice-sdk-core'
+import { jb721TiersHookStoreAbi } from 'juice-sdk-react'
+import { LaunchV2V3ProjectData } from 'packages/v2v3/hooks/transactor/useLaunchProjectTx'
+import { Address, ContractFunctionReturnType } from 'viem'
+import { LaunchV4ProjectGroupedSplit } from '../utils/launchProjectTransformers'
+import { FundAccessLimitGroup } from './fundAccessLimits'
+import { LaunchProjectJBTerminal } from './terminals'
+import { V4CurrencyOption } from './v4CurrencyOption'
+
+/**
+ * @see https://github.com/Bananapus/nana-721-hook/blob/main/src/structs/JB721TierConfig.sol
+ */
+export type JB721TierConfig = Omit<
+ ContractFunctionReturnType<
+ typeof jb721TiersHookStoreAbi,
+ 'view',
+ 'tiersOf'
+ >[0],
+ 'id' | 'votingUnits'
+> & {
+ useReserveBeneficiaryAsDefault: boolean
+ useVotingUnits: boolean
+ votingUnits: number
+}
+
+type JB721InitTiersConfig = {
+ tiers: JB721TierConfig[]
+ currency: number
+ decimals: number
+ prices: Address // JBPrices address
+}
+
+export type JB721TiersHookFlags = {
+ noNewTiersWithReserves: boolean
+ noNewTiersWithVotes: boolean
+ noNewTiersWithOwnerMinting: boolean
+ preventOverspending: boolean
+}
+
+/**
+ * string name;
+ string symbol;
+ string baseUri;
+ IJB721TokenUriResolver tokenUriResolver;
+ string contractUri;
+ JB721InitTiersConfig tiersConfig;
+ address reserveBeneficiary;
+ JB721TiersHookFlags flags;
+ */
+export type JBDeploy721TiersHookConfig = {
+ name: string
+ symbol: string
+ baseUri: string
+ tokenUriResolver: Address //IJB721TokenUriResolver;
+ contractUri: string
+ tiersConfig: JB721InitTiersConfig
+ reserveBeneficiary: Address
+ flags: JB721TiersHookFlags
+}
+
+export type JBPayDataHookRulesetConfig = JBRulesetData & {
+ metadata: JBPayDataHookRulesetMetadata
+ memo?: string
+ fundAccessLimitGroups: FundAccessLimitGroup[]
+ mustStartAtOrAfter?: string // epoch seconds. anything less than "now" will start immediately.
+ terminals: string[]
+ duration: bigint
+ weight: bigint
+ decayPercent: bigint
+ approvalHook: Address
+ splitGroups: LaunchV4ProjectGroupedSplit[]
+}
+
+interface DeployTiered721DelegateData {
+ collectionUri: string
+ collectionName: string
+ collectionSymbol: string
+ currency: V4CurrencyOption
+ tiers: JB721TierConfig[]
+ flags: JB721TiersHookFlags
+}
+
+export interface LaunchProjectWithNftsTxArgs {
+ tiered721DelegateData: DeployTiered721DelegateData
+ projectData: LaunchV2V3ProjectData
+}
+
+export type JB721DelegateLaunchProjectData = {
+ rulesetConfigurations: JBPayDataHookRulesetConfig[]
+ terminalConfigurations: LaunchProjectJBTerminal[]
+ projectMetadataUri: string
+ memo?: string
+}
+
+export type JBPayDataHookRulesetMetadata = Omit<
+ JBRulesetMetadata,
+ 'useDataSourceForPay' | 'dataSource'
+>
diff --git a/src/packages/v4/models/splits.ts b/src/packages/v4/models/splits.ts
new file mode 100644
index 0000000000..7e5fb0e075
--- /dev/null
+++ b/src/packages/v4/models/splits.ts
@@ -0,0 +1,6 @@
+import { JBSplit } from "juice-sdk-core"
+
+export interface GroupedSplits {
+ groupId: G
+ splits: JBSplit[]
+}
diff --git a/src/packages/v4/models/terminals.ts b/src/packages/v4/models/terminals.ts
new file mode 100644
index 0000000000..c5f474928f
--- /dev/null
+++ b/src/packages/v4/models/terminals.ts
@@ -0,0 +1,10 @@
+import { Address } from 'viem';
+
+export type LaunchProjectJBTerminal = {
+ terminal: Address;
+ accountingContextsToAccept: {
+ token: `0x${string}`;
+ decimals: 18;
+ currency: number;
+ }[];
+}
diff --git a/src/packages/v4/utils/launchProject.ts b/src/packages/v4/utils/launchProject.ts
deleted file mode 100644
index 21292b1834..0000000000
--- a/src/packages/v4/utils/launchProject.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import { NATIVE_TOKEN, NATIVE_TOKEN_DECIMALS, SplitGroup } from 'juice-sdk-core'
-import round from 'lodash/round'
-import { V2FundingCycleMetadata } from 'packages/v2/models/fundingCycle'
-import {
- V2V3FundAccessConstraint,
- V2V3FundingCycleData,
-} from 'packages/v2v3/models/fundingCycle'
-import { GroupedSplits } from 'packages/v2v3/models/splits'
-import { V3FundingCycleMetadata } from 'packages/v3/models/fundingCycle'
-import { Address } from 'viem'
-
-export type LaunchV2V3ProjectArgs = [
- string, // _owner
- [string, number], // _projectMetadata [projectMetadataCID, JUICEBOX_MONEY_PROJECT_METADATA_DOMAIN]
- V2V3FundingCycleData, // _data
- V2FundingCycleMetadata | V3FundingCycleMetadata, // _metadata
- string, // _mustStartAtOrAfter
- GroupedSplits[], // _groupedSplits
- V2V3FundAccessConstraint[], // _fundAccessConstraints
- string[], // _terminals
- string, // _memo
-]
-
-export function transformV2V3CreateArgsToV4({
- v2v3Args,
- primaryNativeTerminal,
- currencyTokenAddress,
-}: {
- v2v3Args: LaunchV2V3ProjectArgs
- primaryNativeTerminal: Address
- currencyTokenAddress: Address
-}) {
- const [
- _owner,
- _projectMetadata,
- _data,
- _metadata,
- _mustStartAtOrAfter,
- _groupedSplits,
- _fundAccessConstraints,
- _terminals,
- _memo,
- ] = v2v3Args
-
- const mustStartAtOrAfterNum = parseInt(_mustStartAtOrAfter)
- const now = round(new Date().getTime() / 1000)
-
- const ruleset = {
- mustStartAtOrAfter: mustStartAtOrAfterNum > now ? mustStartAtOrAfterNum : now,
- duration: _data.duration.toNumber(),
- weight: _data.weight.toBigInt(),
- decayPercent: _data.discountRate.toNumber(),
-
- approvalHook: _data.ballot as Address,
-
- metadata: {
- reservedPercent: _metadata.reservedRate.toNumber(),
- redemptionRate: _metadata.redemptionRate.toNumber(),
- baseCurrency: 1, // Not present in v2v3, passing 1 by default
- pausePay: _metadata.pausePay,
- pauseRedeem: _metadata.pauseRedeem,
- pauseCreditTransfers: Boolean(_metadata.global.pauseTransfers),
- allowOwnerMinting: _metadata.allowMinting,
- allowSetCustomToken: false, // Assuming false by default
- allowTerminalMigration: _metadata.allowTerminalMigration,
- allowSetTerminals: _metadata.global.allowSetTerminals,
- allowSetController: _metadata.global.allowSetController,
- allowAddAccountingContext: false, // Not present in v2v3, passing false by default
- allowAddPriceFeed: false, // Not present in v2v3, passing false by default
- ownerMustSendPayouts: false, // Not present in v2v3, passing false by default
- holdFees: _metadata.holdFees,
- useTotalSurplusForRedemptions: _metadata.useTotalOverflowForRedemptions,
- useDataHookForPay: _metadata.useDataSourceForPay,
- useDataHookForRedeem: _metadata.useDataSourceForRedeem,
- dataHook: _metadata.dataSource as Address,
- metadata: 0,
- allowCrosschainSuckerExtension: false,
- },
-
- splitGroups: _groupedSplits.map(group => ({
- groupId:
- group.group === SplitGroup.ETHPayout
- ? BigInt(NATIVE_TOKEN)
- : 1n, // TODO dont hardcode reserved token group as 1n
- splits: group.splits.map(split => ({
- preferAddToBalance: Boolean(split.preferClaimed),
- percent: split.percent,
- projectId: BigInt(parseInt(split.projectId ?? '0x00', 16)),
- beneficiary: split.beneficiary as Address,
- lockedUntil: split.lockedUntil ?? 0,
- hook: split.allocator as Address,
- })),
- })),
-
- fundAccessLimitGroups: _fundAccessConstraints.map(constraint => ({
- terminal: primaryNativeTerminal,
- token: currencyTokenAddress,
- payoutLimits: [
- {
- amount: constraint.distributionLimit.toBigInt(),
- currency: Number(BigInt(NATIVE_TOKEN)), // TODO support USD somehow
- },
- ],
- surplusAllowances: [
- {
- amount: constraint.overflowAllowance.toBigInt(),
- currency: Number(BigInt(NATIVE_TOKEN)),
- },
- ],
- })),
- }
-
- const rulesetConfigurations = [ruleset]
-
- const terminalConfigurations = _terminals.map(terminal => ({
- terminal: terminal as Address,
- accountingContextsToAccept: [
- {
- token: currencyTokenAddress,
- decimals: NATIVE_TOKEN_DECIMALS,
- currency: Number(BigInt(currencyTokenAddress)),
- },
- ],
- }))
-
- return [
- _owner as Address,
- _projectMetadata[0],
- rulesetConfigurations,
- terminalConfigurations,
- _memo,
- ] as const
-}
diff --git a/src/packages/v4/utils/launchProjectTransformers.ts b/src/packages/v4/utils/launchProjectTransformers.ts
new file mode 100644
index 0000000000..d5cfb39edb
--- /dev/null
+++ b/src/packages/v4/utils/launchProjectTransformers.ts
@@ -0,0 +1,195 @@
+import {
+ JBSplit,
+ NATIVE_TOKEN,
+ NATIVE_TOKEN_DECIMALS,
+ SplitGroup,
+} from 'juice-sdk-core'
+import round from 'lodash/round'
+import { V2FundingCycleMetadata } from 'packages/v2/models/fundingCycle'
+import {
+ V2V3FundAccessConstraint,
+ V2V3FundingCycleData,
+} from 'packages/v2v3/models/fundingCycle'
+import { GroupedSplits as V2V3GroupedSplits } from 'packages/v2v3/models/splits'
+import { V3FundingCycleMetadata } from 'packages/v3/models/fundingCycle'
+import { Address } from 'viem'
+import { FundAccessLimitGroup } from '../models/fundAccessLimits'
+import { GroupedSplits as V4GroupedSplits } from '../models/splits'
+import { LaunchProjectJBTerminal } from '../models/terminals'
+
+export type LaunchV2V3ProjectArgs = [
+ string, // _owner
+ [string, number], // _projectMetadata [projectMetadataCID, JUICEBOX_MONEY_PROJECT_METADATA_DOMAIN]
+ V2V3FundingCycleData, // _fundingCycleData
+ V2FundingCycleMetadata | V3FundingCycleMetadata, // _fundingCycleMetadata
+ string, // _mustStartAtOrAfter
+ V2V3GroupedSplits[], // _groupedSplits
+ V2V3FundAccessConstraint[], // _fundAccessConstraints
+ string[], // _terminals
+ string, // _memo
+]
+
+export function transformV2V3CreateArgsToV4({
+ v2v3Args,
+ primaryNativeTerminal,
+ currencyTokenAddress,
+}: {
+ v2v3Args: LaunchV2V3ProjectArgs
+ primaryNativeTerminal: Address
+ currencyTokenAddress: Address
+}) {
+ const [
+ _owner,
+ _projectMetadata,
+ _fundingCycleData,
+ _fundingCycleMetadata,
+ _mustStartAtOrAfter,
+ _groupedSplits,
+ _fundAccessConstraints,
+ _terminals,
+ _memo,
+ ] = v2v3Args
+
+ const mustStartAtOrAfterNum = parseInt(_mustStartAtOrAfter)
+ const now = round(new Date().getTime() / 1000)
+
+ const ruleset = {
+ mustStartAtOrAfter:
+ mustStartAtOrAfterNum > now ? mustStartAtOrAfterNum : now,
+ duration: _fundingCycleData.duration.toNumber(),
+ weight: _fundingCycleData.weight.toBigInt(),
+ decayPercent: _fundingCycleData.discountRate.toNumber(),
+
+ approvalHook: _fundingCycleData.ballot as Address,
+
+ metadata: transformFCMetadataToRulesetMetadata({
+ fundingCycleMetadata: _fundingCycleMetadata,
+ }),
+
+ splitGroups: transformV2V3SplitsToV4({ v2v3Splits: _groupedSplits }),
+
+ fundAccessLimitGroups: transformV2V3FundAccessConstraintsToV4({
+ v2V3FundAccessConstraints: _fundAccessConstraints,
+ primaryNativeTerminal,
+ currencyTokenAddress,
+ }),
+ }
+
+ const rulesetConfigurations = [ruleset]
+
+ const terminalConfigurations = generateV4LaunchTerminalConfigurationsArg({
+ terminals: _terminals,
+ currencyTokenAddress,
+ })
+
+ return [
+ _owner as Address,
+ _projectMetadata[0],
+ rulesetConfigurations,
+ terminalConfigurations,
+ _memo,
+ ] as const
+}
+
+export function transformFCMetadataToRulesetMetadata({
+ fundingCycleMetadata,
+}: {
+ fundingCycleMetadata: V2FundingCycleMetadata | V3FundingCycleMetadata
+}) {
+ return {
+ reservedPercent: fundingCycleMetadata.reservedRate.toNumber(),
+ redemptionRate: fundingCycleMetadata.redemptionRate.toNumber(),
+ baseCurrency: 1, // Not present in v2v3, passing 1 by default
+ pausePay: fundingCycleMetadata.pausePay,
+ pauseRedeem: fundingCycleMetadata.pauseRedeem,
+ pauseCreditTransfers: Boolean(fundingCycleMetadata.global.pauseTransfers),
+ allowOwnerMinting: fundingCycleMetadata.allowMinting,
+ allowSetCustomToken: false, // Assuming false by default
+ allowTerminalMigration: fundingCycleMetadata.allowTerminalMigration,
+ allowSetTerminals: fundingCycleMetadata.global.allowSetTerminals,
+ allowSetController: fundingCycleMetadata.global.allowSetController,
+ allowAddAccountingContext: false, // Not present in v2v3, passing false by default
+ allowAddPriceFeed: false, // Not present in v2v3, passing false by default
+ ownerMustSendPayouts: false, // Not present in v2v3, passing false by default
+ holdFees: fundingCycleMetadata.holdFees,
+ useTotalSurplusForRedemptions:
+ fundingCycleMetadata.useTotalOverflowForRedemptions,
+ useDataHookForPay: fundingCycleMetadata.useDataSourceForPay,
+ useDataHookForRedeem: fundingCycleMetadata.useDataSourceForRedeem,
+ dataHook: fundingCycleMetadata.dataSource as Address,
+ metadata: 0,
+ allowCrosschainSuckerExtension: false,
+ }
+}
+
+type LaunchProjectJBSplit = Omit & { percent: number }
+
+export type LaunchV4ProjectGroupedSplit = Omit<
+ V4GroupedSplits,
+ 'splits' | 'groupId'
+> & { splits: LaunchProjectJBSplit[], groupId: bigint }
+
+export function transformV2V3SplitsToV4({
+ v2v3Splits,
+}: {
+ v2v3Splits: V2V3GroupedSplits[]
+}): LaunchV4ProjectGroupedSplit[] {
+ return v2v3Splits.map(group => ({
+ groupId:
+ group.group === SplitGroup.ETHPayout ? BigInt(NATIVE_TOKEN) : 1n, // TODO dont hardcode reserved token group as 1n
+ splits: group.splits.map(split => ({
+ preferAddToBalance: Boolean(split.preferClaimed),
+ percent: split.percent,
+ projectId: BigInt(parseInt(split.projectId ?? '0x00', 16)),
+ beneficiary: split.beneficiary as Address,
+ lockedUntil: split.lockedUntil ?? 0,
+ hook: split.allocator as Address,
+ })),
+ }))
+}
+
+export function transformV2V3FundAccessConstraintsToV4({
+ v2V3FundAccessConstraints,
+ primaryNativeTerminal,
+ currencyTokenAddress,
+}: {
+ v2V3FundAccessConstraints: V2V3FundAccessConstraint[]
+ primaryNativeTerminal: Address
+ currencyTokenAddress: Address
+}): FundAccessLimitGroup[] {
+ return v2V3FundAccessConstraints.map(constraint => ({
+ terminal: primaryNativeTerminal,
+ token: currencyTokenAddress,
+ payoutLimits: [
+ {
+ amount: constraint.distributionLimit.toBigInt(),
+ currency: Number(BigInt(NATIVE_TOKEN)), // TODO support USD somehow
+ },
+ ],
+ surplusAllowances: [
+ {
+ amount: constraint.overflowAllowance.toBigInt(),
+ currency: Number(BigInt(NATIVE_TOKEN)),
+ },
+ ],
+ }))
+}
+
+function generateV4LaunchTerminalConfigurationsArg({
+ terminals,
+ currencyTokenAddress,
+}: {
+ terminals: string[]
+ currencyTokenAddress: Address
+}): LaunchProjectJBTerminal[] {
+ return terminals.map(terminal => ({
+ terminal: terminal as Address,
+ accountingContextsToAccept: [
+ {
+ token: currencyTokenAddress,
+ decimals: NATIVE_TOKEN_DECIMALS,
+ currency: Number(BigInt(currencyTokenAddress)),
+ },
+ ],
+ }))
+}
diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx
index f88a7c5575..cc99f1436c 100644
--- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx
+++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectTabs/V4ProjectTabs.tsx
@@ -1,8 +1,9 @@
+import { Fragment, useEffect, useMemo, useRef, useState } from 'react'
+
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 { twMerge } from 'tailwind-merge'
import { useProjectPageQueries } from '../hooks/useProjectPageQueries'
import V4AboutPanel from './V4AboutPanel'
@@ -47,6 +48,12 @@ export const V4ProjectTabs = ({ className }: { className?: string }) => {
() => [
{ id: 'activity', name: t`Activity`, panel: },
{ id: 'about', name: t`About`, panel: },
+ // {
+ // id: 'nft_rewards',
+ // name: t`NFTs`,
+ // panel: ,
+ // hideTab: !showNftRewards,
+ // },
{
id: 'cycle_payouts',
name: t`Cycles & Payouts`,
diff --git a/src/redux/slices/editingV2Project/defaultState.ts b/src/redux/slices/editingV2Project/defaultState.ts
index 3467dcf5ac..dacd6c6e16 100644
--- a/src/redux/slices/editingV2Project/defaultState.ts
+++ b/src/redux/slices/editingV2Project/defaultState.ts
@@ -22,6 +22,7 @@ import {
serializeV2V3FundingCycleData,
serializeV2V3FundingCycleMetadata,
} from 'packages/v2v3/utils/serializers'
+import { JB721TiersHookFlags } from 'packages/v4/models/nfts'
import { projectDescriptionTemplate } from 'templates/create/projectDescriptionTemplate'
import { CreateState, ProjectState } from './types'
@@ -92,6 +93,14 @@ export const DEFAULT_NFT_FLAGS: JBTiered721Flags = {
preventOverspending: false,
}
+
+export const DEFAULT_NFT_FLAGS_V4: JB721TiersHookFlags = {
+ noNewTiersWithReserves: false,
+ noNewTiersWithVotes: false,
+ noNewTiersWithOwnerMinting: false,
+ preventOverspending: false,
+}
+
const DEFAULT_PROJECT_METADATA_STATE: ProjectMetadata = {
name: '',
infoUri: '',
diff --git a/src/utils/ipfs.ts b/src/utils/ipfs.ts
index c8ea023760..f13a560715 100644
--- a/src/utils/ipfs.ts
+++ b/src/utils/ipfs.ts
@@ -62,8 +62,8 @@ export function pinataToGatewayUrl(url: string) {
*
* Hex-encoded CIDs are used to store some CIDs on-chain because they are more gas-efficient.
*/
-export function encodeIpfsUri(cid: string) {
- return '0x' + Buffer.from(base58.decode(cid).slice(2)).toString('hex')
+export function encodeIpfsUri(cid: string): `0x${string}` {
+ return `0x${Buffer.from(base58.decode(cid).slice(2)).toString('hex')}`
}
/**
diff --git a/yarn.lock b/yarn.lock
index 9aae589e59..44e92fbd19 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12837,18 +12837,18 @@ jsx-ast-utils@^3.3.5:
object.assign "^4.1.4"
object.values "^1.1.6"
-juice-sdk-core@^11.0.0-alpha:
- version "11.0.0-alpha"
- resolved "https://registry.yarnpkg.com/juice-sdk-core/-/juice-sdk-core-11.0.0-alpha.tgz#dd0228158de3c3a2799ea6ba3b1f22c8c0635d60"
- integrity sha512-wqKAb9f88579CiTZP6MNb08TOStQ4OqgLQYh7cwmONcpon09+A9tyHPCxhgKjGHGevzF80Wv2Z0dPjgujMRI2Q==
+juice-sdk-core@^11.5.0-alpha:
+ version "11.5.0-alpha"
+ resolved "https://registry.yarnpkg.com/juice-sdk-core/-/juice-sdk-core-11.5.0-alpha.tgz#40ae9e4a44a86dc74777842bb18b30ae36373344"
+ integrity sha512-0T/v1OHsG99tO/Zo5pY1tbJzKjh0KB4bEMLAnJSRNOeb6hAKiS77xgcth22uYRRpZc4OLZWFjszR+FC2dAKQUg==
dependencies:
bs58 "^5.0.0"
fpnum "^1.0.0"
-juice-sdk-react@^11.0.0-alpha:
- version "11.0.0-alpha"
- resolved "https://registry.yarnpkg.com/juice-sdk-react/-/juice-sdk-react-11.0.0-alpha.tgz#15b93be75d80e9e83a0f45b4cc8c81d3f1828e49"
- integrity sha512-G0zCPMCozfAK0Y9mxGMtMOYW8Bm3ml5bEEr/pKFbrrxIs+sEUnK4f5CZWiaA6fZlN8QnUbXbIRteFjv+RB2Yzg==
+juice-sdk-react@^11.6.0-alpha:
+ version "11.6.0-alpha"
+ resolved "https://registry.yarnpkg.com/juice-sdk-react/-/juice-sdk-react-11.6.0-alpha.tgz#5ddc6a42f2586ed3b694bde5b8ad76b9ad788b9c"
+ integrity sha512-9ji5COnA0aaqXQOnqbqpohaOXPE7tTNz9J9FlA2d4jG9wlXjYDJrpCAB42g1p1Ojvel6zsLLzuY7aT25UrYFjg==
juice@^10.0.0:
version "10.0.0"