diff --git a/packages/i18n/locales/en/translation.json b/packages/i18n/locales/en/translation.json index 83e649863..fc41fa2f7 100644 --- a/packages/i18n/locales/en/translation.json +++ b/packages/i18n/locales/en/translation.json @@ -227,6 +227,7 @@ "only_passed": "Only passed proposals" }, "emoji": { + "artistPalette": "Artist palette", "baby": "Baby", "babyAngel": "Baby Angel", "ballotBox": "Ballot box", @@ -291,6 +292,7 @@ "cannotCreateCompensationCycleAlreadyActive": "You cannot create a new compensation cycle while one is already active.", "cannotStakeMoreThanYouHave": "You can't stake or unstake more tokens than you have.", "cannotTxZeroTokens": "You can't stake, unstake, or claim zero tokens.", + "cannotUseCreateNftCollectionOnStargaze": "You cannot create a new NFT Collection via this action on Stargaze. Use the embedded browser with Stargaze Studio instead.", "cantSpendMoreThanTreasury": "Can't spend more tokens than are in the treasury ({{amount}} ${{tokenSymbol}}).", "chainNotConnected": "Chain not connected.", "checkInternetOrTryAgain": "Check your internet connection, refresh the page, or try again later.", @@ -981,7 +983,7 @@ "stakedNftsExplanation": "These have been staked by DAO members to earn voting power.", "stakes": "Stakes", "stakingAddress": "Staking Contract", - "stargazeCreateCollectionFirst": "On Stargaze, you must first create the NFT Collection via a DAO proposal, and then you can mint the NFTs. Click below to replace this with the Create NFT Action.", + "stargazeCreateCollectionFirst": "On Stargaze, you must first create the NFT Collection via a DAO proposal, and then you can mint NFTs.", "startedAt": "Started at", "startsAt": "Starts at", "startsIn": "Starts in", diff --git a/packages/stateful/actions/core/chain_governance/ValidatorActions/Component.tsx b/packages/stateful/actions/core/chain_governance/ValidatorActions/Component.tsx index f2c1f1cbb..9c94087cb 100644 --- a/packages/stateful/actions/core/chain_governance/ValidatorActions/Component.tsx +++ b/packages/stateful/actions/core/chain_governance/ValidatorActions/Component.tsx @@ -14,7 +14,7 @@ import { InputLabel, SelectInput, } from '@dao-dao/stateless' -import { ActionComponent } from '@dao-dao/types/actions' +import { ActionComponent, ActionContextType } from '@dao-dao/types/actions' import { getChainAddressForActionOptions, getChainForChainId, @@ -97,14 +97,16 @@ export const ValidatorActionsComponent: ActionComponent = ({ return ( <> - - updateChainValues(chainId, validatorActionTypeUrl) - } - /> + {options.context.type === ActionContextType.Dao && ( + + updateChainValues(chainId, validatorActionTypeUrl) + } + /> + )} ", "name": "", - "symbol": "", - "collectionInfo": { - "type": <"base" | "vending">, - "description": "", - "explicitContent": true | false, - "externalLink": <"LINK" | undefined>, - "image": ", - "royalties": "", - "startTradingDate": "" - } + "symbol": "" } ``` - -`collectionInfo` is only relevant when `chainId` is `stargaze-1` (Stargaze -mainnet) or `elgafar-1` (Stargaze testnet). All other chains only need `name` -and `symbol`. diff --git a/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx b/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx index 984ecebc8..7aa703488 100644 --- a/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx +++ b/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx @@ -1,7 +1,6 @@ import { useCallback } from 'react' -import { useFormContext } from 'react-hook-form' -import { CameraEmoji, ChainProvider } from '@dao-dao/stateless' +import { ArtistPaletteEmoji, ChainPickerInput } from '@dao-dao/stateless' import { ChainId } from '@dao-dao/types' import { ActionComponent, @@ -13,151 +12,87 @@ import { UseTransformToCosmos, } from '@dao-dao/types/actions' import { - MAINNET, - STARGAZE_MAINNET_BASE_MINTER_FACTORY, - STARGAZE_MAINNET_VENDING_MINTER_FACTORY, - STARGAZE_TESTNET_BASE_MINTER_FACTORY, - STARGAZE_TESTNET_VENDING_MINTER_FACTORY, decodePolytoneExecuteMsg, + getChainAddressForActionOptions, getSupportedChainConfig, - makePolytoneExecuteMessage, makeWasmMessage, + maybeMakePolytoneExecuteMessage, objectMatchesStructure, } from '@dao-dao/utils' import { InstantiateNftCollectionAction, InstantiateNftCollectionData, - Trans, } from '../../../../components' +import { useActionOptions } from '../../../react' const Component: ActionComponent = (props) => { - const { watch } = useFormContext() - const chainId = watch((props.fieldNamePrefix + 'chainId') as 'chainId') + const { context } = useActionOptions() return ( - - - + <> + {context.type === ActionContextType.Dao && ( + + )} + + + ) } export const makeCreateNftCollectionAction: ActionMaker< InstantiateNftCollectionData -> = ({ t, address, chain: { chain_id: currentChainId }, context }) => { +> = (options) => { + const { + t, + chain: { chain_id: currentChainId }, + context, + } = options + const useDefaults: UseDefaults = () => ({ chainId: currentChainId, - minter: address, name: '', symbol: '', - collectionInfo: { - type: 'base', - description: '', - explicitContent: false, - image: '', - royalties: 5, - }, }) const useTransformToCosmos: UseTransformToCosmos< InstantiateNftCollectionData > = () => - useCallback( - ({ - chainId, - name, - symbol, - collectionInfo: { - type, - description, - explicitContent, - externalLink, - image, - startTradingDate, - royalties, - } = {} as any, - }: InstantiateNftCollectionData) => { - const creator = - context.type !== ActionContextType.Dao || currentChainId === chainId - ? address - : context.info.polytoneProxies[chainId] ?? '' + useCallback(({ chainId, name, symbol }: InstantiateNftCollectionData) => { + if ( + chainId === ChainId.StargazeMainnet || + chainId === ChainId.StargazeTestnet + ) { + throw new Error(t('error.cannotUseCreateNftCollectionOnStargaze')) + } - const createMsg = makeWasmMessage({ - wasm: - chainId === ChainId.StargazeMainnet || - chainId === ChainId.StargazeTestnet - ? { - execute: { - contract_addr: MAINNET - ? type === 'base' - ? STARGAZE_MAINNET_BASE_MINTER_FACTORY - : STARGAZE_MAINNET_VENDING_MINTER_FACTORY - : type === 'base' - ? STARGAZE_TESTNET_BASE_MINTER_FACTORY - : STARGAZE_TESTNET_VENDING_MINTER_FACTORY, - funds: [], - msg: { - create_minter: { - init_msg: {}, - collection_params: { - code_id: 999999, - name, - symbol, - info: { - creator, - description, - image, - external_link: externalLink, - explicit_content: explicitContent, - start_trading_time: - startTradingDate && - !isNaN(Date.parse(startTradingDate)) - ? // milliseconds => nanoseconds - Math.round( - new Date(startTradingDate).getTime() * 1e6 - ).toString() - : null, - royalty_info: royalties - ? { - payment_address: creator, - share: (royalties / 100).toFixed(2), - } - : null, - }, - }, - }, - }, - }, - } - : { - instantiate: { - admin: creator, - code_id: - getSupportedChainConfig(chainId)?.codeIds.Cw721Base ?? -1, - funds: [], - label: name, - msg: { - minter: creator, - name, - symbol, - }, - }, - }, - }) + const creator = getChainAddressForActionOptions(options, chainId) - if (chainId === currentChainId) { - return createMsg - } else { - return makePolytoneExecuteMessage(currentChainId, chainId, createMsg) - } - }, - [] - ) + return maybeMakePolytoneExecuteMessage( + currentChainId, + chainId, + makeWasmMessage({ + wasm: { + instantiate: { + admin: creator, + code_id: + getSupportedChainConfig(chainId)?.codeIds.Cw721Base ?? -1, + funds: [], + label: name, + msg: { + minter: creator, + name, + symbol, + }, + }, + }, + }) + ) + }, []) const useDecodedCosmosMsg: UseDecodedCosmosMsg< InstantiateNftCollectionData @@ -169,123 +104,36 @@ export const makeCreateNftCollectionAction: ActionMaker< msg = decodedPolytone.msg } - if ( - chainId === ChainId.StargazeMainnet || - chainId === ChainId.StargazeTestnet - ) { - if ( - !objectMatchesStructure(msg, { - wasm: { - execute: { - contract_addr: {}, - funds: {}, - msg: { - create_minter: { - init_msg: {}, - collection_params: {}, - }, - }, - }, + return objectMatchesStructure(msg, { + wasm: { + instantiate: { + code_id: {}, + label: {}, + msg: { + minter: {}, + name: {}, + symbol: {}, + }, + funds: {}, + }, + }, + }) + ? { + match: true, + data: { + chainId, + name: msg.wasm.instantiate.name, + symbol: msg.wasm.instantiate.symbol, }, - }) - ) { - return { - match: false, } - } - - const factory = msg.wasm.execute.contract_addr - const type = - chainId === ChainId.StargazeMainnet - ? factory === STARGAZE_MAINNET_BASE_MINTER_FACTORY - ? 'base' - : STARGAZE_MAINNET_VENDING_MINTER_FACTORY - ? 'vending' - : undefined - : factory === STARGAZE_TESTNET_BASE_MINTER_FACTORY - ? 'base' - : STARGAZE_TESTNET_VENDING_MINTER_FACTORY - ? 'vending' - : undefined - if (!type) { - return { + : { match: false, } - } - - const collectionParams = - msg.wasm.execute.msg.create_minter.collection_params - - return objectMatchesStructure(collectionParams, { - code_id: {}, - name: {}, - symbol: {}, - info: { - creator: {}, - description: {}, - image: {}, - }, - }) - ? { - match: true, - data: { - chainId, - name: collectionParams.name, - symbol: collectionParams.symbol, - collectionInfo: { - type, - description: collectionParams.info.description, - explicitContent: - collectionParams.info.explicit_content || false, - externalLink: collectionParams.info.external_link, - image: collectionParams.info.image, - royalties: collectionParams.info.royalty_info - ? parseFloat(collectionParams.info.royalty_info.share) * 100 - : 0, - startTradingDate: collectionParams.info.start_trading_time - ? new Date( - // nanoseconds => milliseconds - Number(collectionParams.info.start_trading_time) / 1e6 - ).toLocaleString() - : undefined, - }, - }, - } - : { - match: false, - } - } else { - return objectMatchesStructure(msg, { - wasm: { - instantiate: { - code_id: {}, - label: {}, - msg: { - minter: {}, - name: {}, - symbol: {}, - }, - funds: {}, - }, - }, - }) - ? { - match: true, - data: { - chainId, - name: msg.wasm.instantiate.name, - symbol: msg.wasm.instantiate.symbol, - }, - } - : { - match: false, - } - } } return { key: ActionKey.CreateNftCollection, - Icon: CameraEmoji, + Icon: ArtistPaletteEmoji, label: t('title.createNftCollection'), description: t('info.createNftCollectionDescription', { context: context.type, diff --git a/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx b/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx index ebe9d0b8f..984a16a4f 100644 --- a/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx +++ b/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx @@ -3,37 +3,30 @@ import { useFormContext } from 'react-hook-form' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' -import { Button, useSupportedChainContext } from '@dao-dao/stateless' +import { useSupportedChainContext } from '@dao-dao/stateless' +import { ActionComponent, ActionContextType, ActionKey } from '@dao-dao/types' import { - ActionComponent, - ActionContextType, - ActionKey, - ChainId, -} from '@dao-dao/types' -import { instantiateSmartContract, processError } from '@dao-dao/utils' + getChainAddressForActionOptions, + instantiateSmartContract, + processError, +} from '@dao-dao/utils' -import { Trans } from '../../../../components' import { useWallet } from '../../../../hooks' -import { useActionForKey, useActionOptions } from '../../../react' +import { useActionOptions } from '../../../react' import { InstantiateNftCollection as StatelessInstantiateNftCollection } from './stateless/InstantiateNftCollection' import { MintNftData } from './types' export const InstantiateNftCollection: ActionComponent = (props) => { const { t } = useTranslation() const { watch, setValue } = useFormContext() - const { - context, - chain: { chain_id: nativeChainId }, - address, - } = useActionOptions() - - const [instantiating, setInstantiating] = useState(false) - + const options = useActionOptions() const { chainId, config: { codeIds }, } = useSupportedChainContext() + const [instantiating, setInstantiating] = useState(false) + const { address: walletAddress, getSigningCosmWasmClient } = useWallet({ chainId, }) @@ -62,11 +55,7 @@ export const InstantiateNftCollection: ActionComponent = (props) => { setInstantiating(true) try { - const minter = - context.type !== ActionContextType.Dao || nativeChainId === chainId - ? address - : context.info.polytoneProxies[chainId] ?? '' - + const minter = getChainAddressForActionOptions(options, chainId) const contractAddress = await instantiateSmartContract( signingCosmWasmClient, walletAddress, @@ -99,7 +88,7 @@ export const InstantiateNftCollection: ActionComponent = (props) => { toast.success(t('success.nftCollectionContractInstantiated')) // Add display NFT action if in a DAO. - if (props.isCreating && context.type === ActionContextType.Dao) { + if (props.isCreating && options.context.type === ActionContextType.Dao) { props.addAction({ actionKey: ActionKey.ManageCw721, data: { @@ -117,40 +106,12 @@ export const InstantiateNftCollection: ActionComponent = (props) => { } } - const createNftCollectionActionDefaults = useActionForKey( - ActionKey.CreateNftCollection - )?.action.useDefaults() - - return chainId === ChainId.StargazeMainnet || - chainId === ChainId.StargazeTestnet ? ( - <> -

- {t('info.stargazeCreateCollectionFirst')} -

- - - - ) : ( + return ( ) diff --git a/packages/stateful/actions/core/nfts/MintNft/index.tsx b/packages/stateful/actions/core/nfts/MintNft/index.tsx index 9c228eb5a..fc450d197 100644 --- a/packages/stateful/actions/core/nfts/MintNft/index.tsx +++ b/packages/stateful/actions/core/nfts/MintNft/index.tsx @@ -76,6 +76,13 @@ const Component: ActionComponent = (props) => { (props.fieldNamePrefix + 'mintMsg.owner') as 'mintMsg.owner', newAddress ) + + // Also update instantiate chain ID. + setValue( + (props.fieldNamePrefix + + 'instantiateData.chainId') as 'instantiateData.chainId', + chainId + ) }} /> )} @@ -144,8 +151,8 @@ export const makeMintNftAction: ActionMaker = ({ contractChosen: false, collectionAddress: undefined, - instantiateMsg: { - minter: address, + instantiateData: { + chainId: currentChainId, name: '', symbol: '', }, diff --git a/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.stories.tsx b/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.stories.tsx index 5e56db280..231b3e3b1 100644 --- a/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.stories.tsx +++ b/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.stories.tsx @@ -7,7 +7,6 @@ import { makeReactHookFormDecorator, } from '@dao-dao/storybook' -import { Trans } from '../../../../../components/Trans' import { MintNftData } from '../types' import { InstantiateNftCollection } from './InstantiateNftCollection' @@ -49,6 +48,5 @@ Default.args = { options: { onInstantiate: async () => alert('instantiate'), instantiating: false, - Trans, }, } diff --git a/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.tsx b/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.tsx index 2dc38a137..89da41742 100644 --- a/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.tsx +++ b/packages/stateful/actions/core/nfts/MintNft/stateless/InstantiateNftCollection.tsx @@ -2,32 +2,44 @@ import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { Button } from '@dao-dao/stateless' -import { ActionComponent } from '@dao-dao/types' +import { ActionComponent, ChainId } from '@dao-dao/types' import { InstantiateNftCollectionAction } from '../../../../../components' -import { InstantiateOptions } from '../types' +import { InstantiateOptions, MintNftData } from '../types' // Form displayed when the user is instantiating a new NFT collection. export const InstantiateNftCollection: ActionComponent = ( props ) => { const { t } = useTranslation() - const { trigger } = useFormContext() + const { trigger, watch } = useFormContext() + const chainId = watch( + (props.fieldNamePrefix + + 'instantiateData.chainId') as 'instantiateData.chainId' + ) return (