From e79c4af15584fcd1c075ad2d4bb4563ee6be9468 Mon Sep 17 00:00:00 2001 From: noah Date: Thu, 9 Nov 2023 07:55:01 -0800 Subject: [PATCH 1/4] Added missing version for Neutron's latest v0.2.1 contracts. (#1458) --- .../actions/core/dao_appearance/ManageWidgets/index.tsx | 3 ++- .../actions/core/dao_governance/ManageStorageItems/index.tsx | 3 ++- packages/stateful/actions/core/nfts/ManageCw721/index.tsx | 3 ++- packages/types/chain.ts | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/stateful/actions/core/dao_appearance/ManageWidgets/index.tsx b/packages/stateful/actions/core/dao_appearance/ManageWidgets/index.tsx index 2ec0888d0..2b62f34f9 100644 --- a/packages/stateful/actions/core/dao_appearance/ManageWidgets/index.tsx +++ b/packages/stateful/actions/core/dao_appearance/ManageWidgets/index.tsx @@ -71,7 +71,8 @@ export const makeManageWidgetsAction: ActionMaker = ({ // V1 DAOs and V2-alpha DAOs use a value key of `addr`, the rest use `value`. const valueKey = context.info.coreVersion === ContractVersion.V1 || - context.info.coreVersion === ContractVersion.V2Alpha + context.info.coreVersion === ContractVersion.V2Alpha || + context.info.coreVersion === ContractVersion.NeutronV021 ? 'addr' : 'value' diff --git a/packages/stateful/actions/core/dao_governance/ManageStorageItems/index.tsx b/packages/stateful/actions/core/dao_governance/ManageStorageItems/index.tsx index 3f0358299..fa423be79 100644 --- a/packages/stateful/actions/core/dao_governance/ManageStorageItems/index.tsx +++ b/packages/stateful/actions/core/dao_governance/ManageStorageItems/index.tsx @@ -63,7 +63,8 @@ export const makeManageStorageItemsAction: ActionMaker< // V1 DAOs and V2-alpha DAOs use a value key of `addr`, V2-beta uses `value`. const valueKey = context.info.coreVersion === ContractVersion.V1 || - context.info.coreVersion === ContractVersion.V2Alpha + context.info.coreVersion === ContractVersion.V2Alpha || + context.info.coreVersion === ContractVersion.NeutronV021 ? 'addr' : 'value' diff --git a/packages/stateful/actions/core/nfts/ManageCw721/index.tsx b/packages/stateful/actions/core/nfts/ManageCw721/index.tsx index 82b43b53b..844e2daa2 100644 --- a/packages/stateful/actions/core/nfts/ManageCw721/index.tsx +++ b/packages/stateful/actions/core/nfts/ManageCw721/index.tsx @@ -191,7 +191,8 @@ export const makeManageCw721Action: ActionMaker = ({ // V1 DAOs and V2-alpha DAOs use a value key of `addr`, V2-beta uses `value`. const storageItemValueKey = context.info.coreVersion === ContractVersion.V1 || - context.info.coreVersion === ContractVersion.V2Alpha + context.info.coreVersion === ContractVersion.V2Alpha || + context.info.coreVersion === ContractVersion.NeutronV021 ? 'addr' : 'value' diff --git a/packages/types/chain.ts b/packages/types/chain.ts index 9ff74d44b..8b03512e2 100644 --- a/packages/types/chain.ts +++ b/packages/types/chain.ts @@ -65,6 +65,10 @@ export enum ContractVersion { V210 = '2.1.0', // https://github.com/DA0-DA0/dao-contracts/releases/tag/v2.3.0 V230 = '2.3.0', + + // Neutron-specific versions. + // https://github.com/neutron-org/neutron-dao/releases/tag/v0.5.0 + NeutronV021 = '0.2.1', } export enum ChainId { From 48536b72e41f0d5714b3567a35b6e02090fc5f69 Mon Sep 17 00:00:00 2001 From: noah Date: Thu, 9 Nov 2023 07:55:16 -0800 Subject: [PATCH 2/4] Fixed arrow position slightly off. (#1459) --- packages/stateless/components/proposal/ProgressBar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/stateless/components/proposal/ProgressBar.tsx b/packages/stateless/components/proposal/ProgressBar.tsx index af38e8f69..1c34e7877 100644 --- a/packages/stateless/components/proposal/ProgressBar.tsx +++ b/packages/stateless/components/proposal/ProgressBar.tsx @@ -55,12 +55,13 @@ export const ProgressBar = ({ ))} + {caretPosition !== undefined && ( From a162e6bd88b4172365afd5e719cafaf27a683f04 Mon Sep 17 00:00:00 2001 From: noah Date: Thu, 9 Nov 2023 08:00:52 -0800 Subject: [PATCH 3/4] Fixed Press and added support for cross-chain Press installs to support Stargaze DAOs. (#1456) --- packages/i18n/locales/en/translation.json | 6 +- .../chain_governance/GovernanceVote/index.tsx | 5 +- .../ValidatorActions/index.tsx | 9 +- .../core/nfts/CreateNftCollection/index.tsx | 3 + .../nfts/MintNft/InstantiateNftCollection.tsx | 9 +- .../actions/core/nfts/MintNft/index.tsx | 3 +- .../smart_contracting/Instantiate/index.tsx | 2 +- .../core/treasury/ManageStaking/index.tsx | 19 +- .../stateful/InstantiateTokenSwap.tsx | 4 +- .../components/StargazeNftImportModal.tsx | 4 +- .../components/WalletStakingModal.tsx | 4 +- .../stateful/components/WalletTokenCard.tsx | 3 +- .../components/dao/DaoTokenDepositModal.tsx | 3 +- .../components/pages/MeTransactionBuilder.tsx | 3 +- .../hooks/useInstantiateAndExecute.ts | 3 +- packages/stateful/widgets/core.ts | 23 +- .../widgets/MintNft/MintNftRenderer.tsx | 3 +- .../widgets/widgets/Press/PressEditor.tsx | 279 +++++++++++------- .../widgets/widgets/Press/Renderer/index.tsx | 12 +- .../Press/actions/CreatePost/index.tsx | 75 +++-- .../Press/actions/DeletePost/index.tsx | 65 ++-- .../Press/actions/UpdatePost/index.tsx | 80 +++-- .../stateful/widgets/widgets/Press/types.ts | 4 + .../WyndDeposit/WyndDepositRenderer.tsx | 3 +- .../widgets/widgets/WyndDeposit/index.ts | 2 + .../components/buttons/Buttonifier.tsx | 4 +- .../components/inputs/ChainPickerInput.tsx | 22 +- .../components/wallet/ConnectWallet.tsx | 15 +- packages/types/stateless/Buttonifier.tsx | 2 +- packages/types/widgets.ts | 2 + packages/utils/actions.ts | 2 +- 31 files changed, 424 insertions(+), 249 deletions(-) diff --git a/packages/i18n/locales/en/translation.json b/packages/i18n/locales/en/translation.json index c0fd5d38b..56b783422 100644 --- a/packages/i18n/locales/en/translation.json +++ b/packages/i18n/locales/en/translation.json @@ -77,6 +77,7 @@ "createAProposal": "Create a proposal", "createAToken": "Create a token", "createAccount": "Create account", + "createCrossChainAccount": "Create cross-chain account", "createDAO": "Create DAO", "createNftCollection": "Create NFT collection", "createSubDao": "Create SubDAO", @@ -397,8 +398,10 @@ "proposalNotFound": "Proposal not found.", "relayerAlreadySetUp": "Relayer already set up.", "relayerNotSetUp": "Relayer not set up.", + "selectAChainToContinue": "Select a chain to continue.", "simulationFailedInvalidProposalActions": "Simulation failed. Verify your proposal actions are valid.", "stakeInsufficient": "The DAO has {{amount}} ${{tokenSymbol}} staked, which is insufficient.", + "stargazeDaoNoCrossChainAccountsForPress": "This Stargaze DAO has no cross-chain accounts, and Press does not work on Stargaze. Create a cross-chain account for the DAO before setting up Press.", "subDaoAlreadyExists": "SubDAO already exists.", "tokenSwapContractNotChosen": "You must create a token swap or enter the address of an existing token swap before using this action.", "tooFewChoices": "The proposal must have at least two choices.", @@ -762,7 +765,7 @@ "createNftCollectionDescription_gov": "Create a new NFT collection controlled by the chain.", "createNftCollectionDescription_wallet": "Create a new NFT collection controlled by you.", "createPostDescription": "Create a post on the DAO's press.", - "createPressContract": "To publish content, you must first create a press contract.", + "createPressContract": "To set up Press, you must first create a smart contract to manage it.", "createStep1": "Pick a DAO type and name it", "createStep2": "Governance configuration", "createStep3": "Voting configuration", @@ -978,6 +981,7 @@ "searchMessages": "Search messages", "searchNftsPlaceholder": "Find an NFT...", "searchValidatorsPlaceholder": "Find a validator...", + "selectPressChain": "Each post will be minted as an NFT on this chain and then deposited into the DAO's treasury.", "selfRelayDescription": "One or more messages in this proposal require self-relaying across chains since automatic relayers do not exist or are inactive right now.", "setUpDiscordNotificationsTooltip": "Set up Discord notifications", "signedInAs": "Signed in as {{name}}", diff --git a/packages/stateful/actions/core/chain_governance/GovernanceVote/index.tsx b/packages/stateful/actions/core/chain_governance/GovernanceVote/index.tsx index c8fe91d6e..2216ab653 100644 --- a/packages/stateful/actions/core/chain_governance/GovernanceVote/index.tsx +++ b/packages/stateful/actions/core/chain_governance/GovernanceVote/index.tsx @@ -94,12 +94,13 @@ const Component: ActionComponent = (props) => { : constSelector(undefined) ) + const address = getChainAddressForActionOptions(options, chainId) const existingVotesLoading = loadableToLoadingData( useRecoilValueLoadable( - proposalId + proposalId && address ? govProposalVoteSelector({ proposalId: Number(proposalId), - voter: getChainAddressForActionOptions(options, chainId), + voter: address, chainId, }) : constSelector(undefined) diff --git a/packages/stateful/actions/core/chain_governance/ValidatorActions/index.tsx b/packages/stateful/actions/core/chain_governance/ValidatorActions/index.tsx index 3648f5472..b1f644913 100644 --- a/packages/stateful/actions/core/chain_governance/ValidatorActions/index.tsx +++ b/packages/stateful/actions/core/chain_governance/ValidatorActions/index.tsx @@ -44,7 +44,7 @@ export const makeValidatorActionsAction: ActionMaker = ( const getValidatorAddress = (chainId: string) => toValidatorAddress( - getChainAddressForActionOptions(options, chainId), + getChainAddressForActionOptions(options, chainId) || '', getChainForChainId(chainId).bech32_prefix ) @@ -181,8 +181,11 @@ export const makeValidatorActionsAction: ActionMaker = ( const data = useDefaults() - // Check this is a stargate message. - if (!isDecodedStargateMsg(msg)) { + if ( + !thisAddress || + // Check this is a stargate message. + !isDecodedStargateMsg(msg) + ) { return { match: false } } diff --git a/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx b/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx index 7aa703488..3aa6e2351 100644 --- a/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx +++ b/packages/stateful/actions/core/nfts/CreateNftCollection/index.tsx @@ -71,6 +71,9 @@ export const makeCreateNftCollectionAction: ActionMaker< } const creator = getChainAddressForActionOptions(options, chainId) + if (!creator) { + throw new Error(t('error.loadingData')) + } return maybeMakePolytoneExecuteMessage( currentChainId, diff --git a/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx b/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx index 984a16a4f..401aeeae3 100644 --- a/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx +++ b/packages/stateful/actions/core/nfts/MintNft/InstantiateNftCollection.tsx @@ -40,22 +40,19 @@ export const InstantiateNftCollection: ActionComponent = (props) => { toast.error(t('error.loadingData')) return } - if (!walletAddress) { toast.error(t('error.logInToContinue')) return } - - if (!codeIds.Cw721Base) { + const minter = getChainAddressForActionOptions(options, chainId) + if (!codeIds.Cw721Base || !minter) { toast.error(t('error.invalidChain')) return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setInstantiating(true) try { - const minter = getChainAddressForActionOptions(options, chainId) + const signingCosmWasmClient = await getSigningCosmWasmClient() const contractAddress = await instantiateSmartContract( signingCosmWasmClient, walletAddress, diff --git a/packages/stateful/actions/core/nfts/MintNft/index.tsx b/packages/stateful/actions/core/nfts/MintNft/index.tsx index fc450d197..3a27979db 100644 --- a/packages/stateful/actions/core/nfts/MintNft/index.tsx +++ b/packages/stateful/actions/core/nfts/MintNft/index.tsx @@ -70,7 +70,8 @@ const Component: ActionComponent = (props) => { fieldName={props.fieldNamePrefix + 'chainId'} onChange={(chainId) => { // Update recipient to correct address. - const newAddress = getChainAddressForActionOptions(options, chainId) + const newAddress = + getChainAddressForActionOptions(options, chainId) || '' setValue( (props.fieldNamePrefix + 'mintMsg.owner') as 'mintMsg.owner', diff --git a/packages/stateful/actions/core/smart_contracting/Instantiate/index.tsx b/packages/stateful/actions/core/smart_contracting/Instantiate/index.tsx index fc8275c8b..4a4258d82 100644 --- a/packages/stateful/actions/core/smart_contracting/Instantiate/index.tsx +++ b/packages/stateful/actions/core/smart_contracting/Instantiate/index.tsx @@ -199,7 +199,7 @@ const Component: ActionComponent = (props) => { setValue((props.fieldNamePrefix + 'funds') as 'funds', []) setValue( (props.fieldNamePrefix + 'admin') as 'admin', - getChainAddressForActionOptions(options, chainId) + getChainAddressForActionOptions(options, chainId) || '' ) }} /> diff --git a/packages/stateful/actions/core/treasury/ManageStaking/index.tsx b/packages/stateful/actions/core/treasury/ManageStaking/index.tsx index 9ccbe93d5..8291cada5 100644 --- a/packages/stateful/actions/core/treasury/ManageStaking/index.tsx +++ b/packages/stateful/actions/core/treasury/ManageStaking/index.tsx @@ -2,6 +2,7 @@ import { coin, parseCoins } from '@cosmjs/amino' import { useCallback } from 'react' import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' +import { constSelector } from 'recoil' import { nativeDelegationInfoSelector, @@ -189,8 +190,6 @@ const InnerComponent: ActionComponent = (props) => { throw new Error(t('error.missingNativeToken')) } - const address = getChainAddressForActionOptions(options, chainId) - // These need to be loaded via cached loadables to avoid displaying a loader // when this data updates on a schedule. Manually trigger a suspense loader // the first time when the initial data is still loading. @@ -215,15 +214,15 @@ const InnerComponent: ActionComponent = (props) => { ), } + const address = getChainAddressForActionOptions(options, chainId) const loadingNativeDelegationInfo = useCachedLoading( - nativeDelegationInfoSelector({ - chainId, - address, - }), - { - delegations: [], - unbondingDelegations: [], - } + address + ? nativeDelegationInfoSelector({ + chainId, + address, + }) + : constSelector(undefined), + undefined ) const loadingValidators = useCachedLoading( diff --git a/packages/stateful/actions/core/treasury/token_swap/stateful/InstantiateTokenSwap.tsx b/packages/stateful/actions/core/treasury/token_swap/stateful/InstantiateTokenSwap.tsx index c83e816f5..0fb59330f 100644 --- a/packages/stateful/actions/core/treasury/token_swap/stateful/InstantiateTokenSwap.tsx +++ b/packages/stateful/actions/core/treasury/token_swap/stateful/InstantiateTokenSwap.tsx @@ -56,10 +56,7 @@ export const InstantiateTokenSwap: ActionComponent< return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setInstantiating(true) - try { const instantiateMsg: InstantiateMsg = { counterparty_one: { @@ -110,6 +107,7 @@ export const InstantiateTokenSwap: ActionComponent< }, } + const signingCosmWasmClient = await getSigningCosmWasmClient() const contractAddress = await instantiateSmartContract( signingCosmWasmClient, walletAddress, diff --git a/packages/stateful/components/StargazeNftImportModal.tsx b/packages/stateful/components/StargazeNftImportModal.tsx index 3da6b7c75..daba445e2 100644 --- a/packages/stateful/components/StargazeNftImportModal.tsx +++ b/packages/stateful/components/StargazeNftImportModal.tsx @@ -50,10 +50,10 @@ export const InnerStargazeNftImportModal = ({ return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setLoading(true) try { + const signingCosmWasmClient = await getSigningCosmWasmClient() + const selectedNfts = nfts.data.filter((nft) => selected.includes(getIdForNft(nft)) ) diff --git a/packages/stateful/components/WalletStakingModal.tsx b/packages/stateful/components/WalletStakingModal.tsx index 200a3aff4..2ffffd7c9 100644 --- a/packages/stateful/components/WalletStakingModal.tsx +++ b/packages/stateful/components/WalletStakingModal.tsx @@ -106,10 +106,10 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setLoading(true) try { + const signingCosmWasmClient = await getSigningCosmWasmClient() + const microAmount = convertDenomToMicroDenomStringWithDecimals( amount, nativeToken.decimals diff --git a/packages/stateful/components/WalletTokenCard.tsx b/packages/stateful/components/WalletTokenCard.tsx index f52e8db85..be4f0cd1b 100644 --- a/packages/stateful/components/WalletTokenCard.tsx +++ b/packages/stateful/components/WalletTokenCard.tsx @@ -171,10 +171,9 @@ export const WalletTokenCard = (props: TokenCardInfo) => { return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setClaimLoading(true) try { + const signingCosmWasmClient = await getSigningCosmWasmClient() await signingCosmWasmClient.signAndBroadcast( walletAddress, (lazyInfo.loading ? [] : lazyInfo.data.stakingInfo!.stakes).map( diff --git a/packages/stateful/components/dao/DaoTokenDepositModal.tsx b/packages/stateful/components/dao/DaoTokenDepositModal.tsx index c345db4f2..763e10994 100644 --- a/packages/stateful/components/dao/DaoTokenDepositModal.tsx +++ b/packages/stateful/components/dao/DaoTokenDepositModal.tsx @@ -97,8 +97,6 @@ export const DaoTokenDepositModal = ({ return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setLoading(true) try { const microAmount = convertDenomToMicroDenomStringWithDecimals( @@ -107,6 +105,7 @@ export const DaoTokenDepositModal = ({ ) if (token.type === 'native') { + const signingCosmWasmClient = await getSigningCosmWasmClient() await signingCosmWasmClient.sendTokens( address, depositAddress, diff --git a/packages/stateful/components/pages/MeTransactionBuilder.tsx b/packages/stateful/components/pages/MeTransactionBuilder.tsx index c355c5283..c8b3401f7 100644 --- a/packages/stateful/components/pages/MeTransactionBuilder.tsx +++ b/packages/stateful/components/pages/MeTransactionBuilder.tsx @@ -96,13 +96,12 @@ export const MeTransactionBuilder = () => { return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setLoading(true) setError('') setTxHash('') try { + const signingCosmWasmClient = await getSigningCosmWasmClient() const encodeObjects = data.map((msg) => cwMsgToEncodeObject(msg, walletAddress) ) diff --git a/packages/stateful/hooks/useInstantiateAndExecute.ts b/packages/stateful/hooks/useInstantiateAndExecute.ts index be99c0ac0..7d088ccc4 100644 --- a/packages/stateful/hooks/useInstantiateAndExecute.ts +++ b/packages/stateful/hooks/useInstantiateAndExecute.ts @@ -80,8 +80,6 @@ export const useInstantiateAndExecute = ( throw new Error(t('error.logInToContinue')) } - const signingCosmWasmClient = await getSigningCosmWasmClient() - // Get the checksum of the contract code. const checksum = fromHex(codeDetailsLoadable.contents.checksum) // Random salt. @@ -118,6 +116,7 @@ export const useInstantiateAndExecute = ( ), ] + const signingCosmWasmClient = await getSigningCosmWasmClient() const response = (await signingCosmWasmClient.signAndBroadcast( address, messages.map((msg) => cwMsgToEncodeObject(msg, address)), diff --git a/packages/stateful/widgets/core.ts b/packages/stateful/widgets/core.ts index ba11fee40..8b8aec680 100644 --- a/packages/stateful/widgets/core.ts +++ b/packages/stateful/widgets/core.ts @@ -1,23 +1,22 @@ -import { ChainId, Widget } from '@dao-dao/types' +import { Widget } from '@dao-dao/types' import { PressWidget, RetroactiveCompensationWidget, VestingPaymentsWidget, - WyndDepositWidget, } from './widgets' // Add widgets here. -export const getWidgets = (chainId: string): readonly Widget[] => [ - // MintNftWidget, - - VestingPaymentsWidget, - RetroactiveCompensationWidget, - PressWidget, - - // WYND only available on Juno mainnet. - ...(chainId === ChainId.JunoMainnet ? [WyndDepositWidget] : []), -] +export const getWidgets = (chainId: string): readonly Widget[] => + [ + // MintNftWidget, + VestingPaymentsWidget, + RetroactiveCompensationWidget, + PressWidget, + ].filter( + (widget) => + !widget.supportedChainIds || widget.supportedChainIds.includes(chainId) + ) export const getWidgetById = (chainId: string, id: string) => getWidgets(chainId).find((widget) => widget.id === id) diff --git a/packages/stateful/widgets/widgets/MintNft/MintNftRenderer.tsx b/packages/stateful/widgets/widgets/MintNft/MintNftRenderer.tsx index dccfe9ba2..858a71c90 100644 --- a/packages/stateful/widgets/widgets/MintNft/MintNftRenderer.tsx +++ b/packages/stateful/widgets/widgets/MintNft/MintNftRenderer.tsx @@ -66,10 +66,9 @@ export const MintNftRenderer = ({ return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setMinting(true) try { + const signingCosmWasmClient = await getSigningCosmWasmClient() await signingCosmWasmClient.execute( walletAddress, contract, diff --git a/packages/stateful/widgets/widgets/Press/PressEditor.tsx b/packages/stateful/widgets/widgets/Press/PressEditor.tsx index 7bc8a370f..2e0d851dc 100644 --- a/packages/stateful/widgets/widgets/Press/PressEditor.tsx +++ b/packages/stateful/widgets/widgets/Press/PressEditor.tsx @@ -1,148 +1,211 @@ -import { instantiate2Address } from '@cosmjs/cosmwasm-stargate' -import { fromHex, toUtf8 } from '@cosmjs/encoding' -import { useEffect } from 'react' +import { Check } from '@mui/icons-material' +import { useEffect, useState } from 'react' import { useFormContext } from 'react-hook-form' +import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' -import { codeDetailsSelector } from '@dao-dao/state/recoil' import { - Loader, - useCachedLoading, + Button, + ChainPickerInput, + CopyableAddress, useDaoInfoContext, useSupportedChainContext, } from '@dao-dao/stateless' -import { ActionKey, WidgetEditorProps } from '@dao-dao/types' +import { ActionKey, ChainId, WidgetEditorProps } from '@dao-dao/types' import { InstantiateMsg as Cw721InstantiateMsg } from '@dao-dao/types/contracts/Cw721Base' +import { + getChainAddressForActionOptions, + getSupportedChainConfig, + instantiateSmartContract, + processError, +} from '@dao-dao/utils' import { useActionOptions } from '../../../actions' +import { ConnectWallet } from '../../../components/ConnectWallet' +import { useWallet } from '../../../hooks/useWallet' import { PressData } from './types' export const PressEditor = ({ - fieldNamePrefix, - allActionsWithData, - index, + remove, addAction, + fieldNamePrefix, isCreating, }: WidgetEditorProps) => { const { t } = useTranslation() + const actionOptions = useActionOptions() + const { - config: { codeIds }, + config: { polytone }, } = useSupportedChainContext() - const { name: daoName, coreAddress } = useDaoInfoContext() const { - chain: { chain_id: chainId, bech32_prefix: bech32Prefix }, - } = useActionOptions() + name: daoName, + chainId: daoChainId, + polytoneProxies, + } = useDaoInfoContext() const { setValue, setError, clearErrors, watch } = useFormContext() - + const chainId = watch((fieldNamePrefix + 'chainId') as 'chainId') const contract = watch((fieldNamePrefix + 'contract') as 'contract') - // Ensure instantiate2 action exists with the right fields when editing. - const codeId = codeIds.Cw721Base || -1 - const codeDetailsLoading = useCachedLoading( - codeId > -1 - ? codeDetailsSelector({ - chainId, - codeId, - }) - : undefined, - undefined - ) - const salt = 'DAO DAO Press' - const instantiate2ActionExists = - isCreating && - allActionsWithData.some( - ({ actionKey, data }) => - actionKey === ActionKey.Instantiate2 && - data.chainId === chainId && - data.codeId === codeId && - data.salt === salt - ) - // Matches the address of the instantiate2 action. - const generatedContractAddress = - codeDetailsLoading.loading || !codeDetailsLoading.data - ? undefined - : instantiate2Address( - fromHex(codeDetailsLoading.data.checksum), - coreAddress, - toUtf8(salt), - bech32Prefix - ) - + // Auto-select valid chain ID if undefined. useEffect(() => { - // If not creating, data not loaded yet, or action exists and contract - // already set, do nothing. - if ( - !isCreating || - !addAction || - codeDetailsLoading.loading || - !codeDetailsLoading.data || - !generatedContractAddress || - (instantiate2ActionExists && contract === generatedContractAddress) - ) { + const defaultChainId = + daoChainId === ChainId.StargazeMainnet + ? Object.keys(polytoneProxies)[0] + : daoChainId + if (!chainId && defaultChainId) { + setValue((fieldNamePrefix + 'chainId') as 'chainId', defaultChainId) + } + }, [chainId, daoChainId, setValue, fieldNamePrefix, polytoneProxies]) + + const { + address: walletAddress = '', + getSigningCosmWasmClient, + chain, + } = useWallet({ + chainId, + }) + + const [instantiating, setInstantiating] = useState(false) + const instantiate = async () => { + if (!walletAddress) { + toast.error(t('error.logInToContinue')) + return + } + if (!chainId) { + toast.error(t('error.selectAChainToContinue')) + return + } + const minter = getChainAddressForActionOptions(actionOptions, chainId) + if (!minter) { + toast.error(t('error.addressNotFoundOnChain')) return } - const name = `${daoName}'s Press` - // Otherwise add the instantiate2 action. - setValue( - (fieldNamePrefix + 'contract') as 'contract', - generatedContractAddress - ) - addAction( - { - actionKey: ActionKey.Instantiate2, - data: { - chainId, - admin: coreAddress, - codeId, - label: name, - message: JSON.stringify( + setInstantiating(true) + try { + const codeId = getSupportedChainConfig(chainId)?.codeIds?.Cw721Base + const signingCosmWasmClient = await getSigningCosmWasmClient() + + const name = `${daoName}'s Press` + const contractAddress = codeId + ? await instantiateSmartContract( + signingCosmWasmClient, + walletAddress, + codeId, + name, { - minter: coreAddress, - name, + minter, + name: name, symbol: 'PRESS', - } as Cw721InstantiateMsg, - null, - 2 - ), - salt, - funds: [], - }, - }, - index - ) - }, [ - addAction, - bech32Prefix, - chainId, - codeDetailsLoading, - codeId, - codeIds.Cw721Base, - contract, - coreAddress, - daoName, - isCreating, - fieldNamePrefix, - index, - instantiate2ActionExists, - salt, - setValue, - generatedContractAddress, - ]) - - // Prevent action from being submitted if the contract has not yet been - // created. + } as Cw721InstantiateMsg + ) + : undefined + + // Should never happen. + if (!contractAddress) { + throw new Error(t('error.loadingData')) + } + + setValue((fieldNamePrefix + 'chainId') as 'chainId', chain.chain_id) + setValue((fieldNamePrefix + 'contract') as 'contract', contractAddress) + + toast.success(t('success.created')) + } catch (err) { + console.error(err) + toast.error(processError(err)) + } finally { + setInstantiating(false) + } + } + + // Prevent action from being submitted if the chain ID has not yet been set or + // the contract has not yet been created. useEffect(() => { - if (!contract) { + if (!chainId || !contract) { setError((fieldNamePrefix + 'contract') as 'contract', { type: 'manual', }) } else { clearErrors((fieldNamePrefix + 'contract') as 'contract') } - }, [setError, clearErrors, t, contract, fieldNamePrefix]) + }, [setError, clearErrors, t, contract, fieldNamePrefix, chainId]) - return <>{!contract && } + const stargazeDaoNoCrossChainAccounts = + (daoChainId === ChainId.StargazeMainnet || + daoChainId === ChainId.StargazeTestnet) && + !Object.keys(polytoneProxies).length + + return ( +
+ {/* If DAO on Stargaze and has no cross-chain accounts, show error. */} + {isCreating && stargazeDaoNoCrossChainAccounts ? ( + <> +

+ {t('error.stargazeDaoNoCrossChainAccountsForPress')} +

+ + {remove && addAction && ( + + )} + + ) : ( + <> +

{t('title.chain')}

+

+ {t('info.selectPressChain')} +

+ + + +
+

+ {contract + ? t('info.createdPressContract') + : t('info.createPressContract')} +

+ + {contract && } +
+ + {contract ? ( + + ) : walletAddress ? ( + + ) : ( + + )} + + )} +
+ ) } diff --git a/packages/stateful/widgets/widgets/Press/Renderer/index.tsx b/packages/stateful/widgets/widgets/Press/Renderer/index.tsx index 27f25fa1b..9a8936dba 100644 --- a/packages/stateful/widgets/widgets/Press/Renderer/index.tsx +++ b/packages/stateful/widgets/widgets/Press/Renderer/index.tsx @@ -1,6 +1,5 @@ import { useCachedLoading, - useChain, useDaoInfoContext, useDaoNavHelpers, } from '@dao-dao/stateless' @@ -15,19 +14,22 @@ import { PressData } from '../types' import { Renderer as StatelessRenderer } from './Renderer' export const Renderer = ({ - variables: { contract }, + variables: { chainId: configuredChainId, contract }, }: WidgetRendererProps) => { - const { chain_id: chainId } = useChain() - const { coreAddress } = useDaoInfoContext() + const { chainId: daoChainId, coreAddress } = useDaoInfoContext() const { getDaoProposalPath } = useDaoNavHelpers() const { isMember = false } = useMembership({ coreAddress, }) + // The chain that Press is set up on. If chain ID is undefined, default to + // native DAO chain for backwards compatibility. + const pressChainId = configuredChainId || daoChainId + const postsLoading = useCachedLoading( postsSelector({ contractAddress: contract, - chainId, + chainId: pressChainId, }), [] ) diff --git a/packages/stateful/widgets/widgets/Press/actions/CreatePost/index.tsx b/packages/stateful/widgets/widgets/Press/actions/CreatePost/index.tsx index 23d6381ab..8bc903cec 100644 --- a/packages/stateful/widgets/widgets/Press/actions/CreatePost/index.tsx +++ b/packages/stateful/widgets/widgets/Press/actions/CreatePost/index.tsx @@ -4,14 +4,19 @@ import { useFormContext } from 'react-hook-form' import { MemoEmoji, useCachedLoading } from '@dao-dao/stateless' import { ActionComponent, - ActionContextType, ActionKey, ActionMaker, UseDecodedCosmosMsg, UseDefaults, UseTransformToCosmos, } from '@dao-dao/types/actions' -import { makeWasmMessage, objectMatchesStructure } from '@dao-dao/utils' +import { + decodePolytoneExecuteMsg, + getChainAddressForActionOptions, + makeWasmMessage, + maybeMakePolytoneExecuteMessage, + objectMatchesStructure, +} from '@dao-dao/utils' import { postSelector } from '../../state' import { PressData } from '../../types' @@ -55,17 +60,31 @@ const Component: ActionComponent = (props) => { } export const makeCreatePostActionMaker = - ({ contract }: PressData): ActionMaker => - ({ t, context, address }) => { - // Only available in DAO context. - if (context.type !== ActionContextType.Dao) { - return null - } + ({ + contract, + chainId: configuredChainId, + }: PressData): ActionMaker => + (options) => { + const { + t, + chain: { chain_id: daoChainId }, + } = options + + // The chain that Press is set up on. If chain ID is undefined, default to + // native DAO chain for backwards compatibility. + const pressChainId = configuredChainId || daoChainId const useDecodedCosmosMsg: UseDecodedCosmosMsg = ( msg: Record - ) => - objectMatchesStructure(msg, { + ) => { + let chainId = daoChainId + const decodedPolytone = decodePolytoneExecuteMsg(chainId, msg) + if (decodedPolytone.match) { + chainId = decodedPolytone.chainId + msg = decodedPolytone.msg + } + + return objectMatchesStructure(msg, { wasm: { execute: { contract_addr: {}, @@ -80,8 +99,8 @@ export const makeCreatePostActionMaker = }, }, }) && - msg.wasm.execute.contract_addr === contract && - msg.wasm.execute.msg.mint.token_uri + msg.wasm.execute.contract_addr === contract && + msg.wasm.execute.msg.mint.token_uri ? { match: true, data: { @@ -93,25 +112,33 @@ export const makeCreatePostActionMaker = : { match: false, } + } const useTransformToCosmos: UseTransformToCosmos = () => useCallback( ({ tokenId, tokenUri }) => - makeWasmMessage({ - wasm: { - execute: { - contract_addr: contract, - funds: [], - msg: { - mint: { - owner: address, - token_id: tokenId, - token_uri: tokenUri, + maybeMakePolytoneExecuteMessage( + daoChainId, + pressChainId, + makeWasmMessage({ + wasm: { + execute: { + contract_addr: contract, + funds: [], + msg: { + mint: { + owner: getChainAddressForActionOptions( + options, + pressChainId + ), + token_id: tokenId, + token_uri: tokenUri, + }, }, }, }, - }, - }), + }) + ), [] ) diff --git a/packages/stateful/widgets/widgets/Press/actions/DeletePost/index.tsx b/packages/stateful/widgets/widgets/Press/actions/DeletePost/index.tsx index c0b271659..a65eb25b6 100644 --- a/packages/stateful/widgets/widgets/Press/actions/DeletePost/index.tsx +++ b/packages/stateful/widgets/widgets/Press/actions/DeletePost/index.tsx @@ -5,14 +5,18 @@ import { constSelector } from 'recoil' import { TrashEmoji, useCachedLoading } from '@dao-dao/stateless' import { ActionComponent, - ActionContextType, ActionKey, ActionMaker, UseDecodedCosmosMsg, UseDefaults, UseTransformToCosmos, } from '@dao-dao/types/actions' -import { makeWasmMessage, objectMatchesStructure } from '@dao-dao/utils' +import { + decodePolytoneExecuteMsg, + makeWasmMessage, + maybeMakePolytoneExecuteMessage, + objectMatchesStructure, +} from '@dao-dao/utils' import { useActionOptions } from '../../../../../actions' import { postSelector, postsSelector } from '../../state' @@ -24,14 +28,18 @@ const useDefaults: UseDefaults = () => ({ }) export const makeDeletePostActionMaker = ({ + chainId: configuredChainId, contract, }: PressData): ActionMaker => { // Make outside of the maker function returned below so it doesn't get // redefined and thus remounted on every render. const Component: ActionComponent = (props) => { const { - chain: { chain_id: chainId }, + chain: { chain_id: daoChainId }, } = useActionOptions() + // The chain that Press is set up on. If chain ID is undefined, default to + // native DAO chain for backwards compatibility. + const pressChainId = configuredChainId || daoChainId const { watch } = useFormContext() const id = watch((props.fieldNamePrefix + 'id') as 'id') @@ -39,7 +47,7 @@ export const makeDeletePostActionMaker = ({ const postsLoading = useCachedLoading( postsSelector({ contractAddress: contract, - chainId, + chainId: pressChainId, }), [] ) @@ -68,16 +76,22 @@ export const makeDeletePostActionMaker = ({ ) } - return ({ t, context }) => { - // Only available in DAO context. - if (context.type !== ActionContextType.Dao) { - return null - } + return ({ t, chain: { chain_id: daoChainId } }) => { + // The chain that Press is set up on. If chain ID is undefined, default to + // native DAO chain for backwards compatibility. + const pressChainId = configuredChainId || daoChainId const useDecodedCosmosMsg: UseDecodedCosmosMsg = ( msg: Record - ) => - objectMatchesStructure(msg, { + ) => { + let chainId = daoChainId + const decodedPolytone = decodePolytoneExecuteMsg(chainId, msg) + if (decodedPolytone.match) { + chainId = decodedPolytone.chainId + msg = decodedPolytone.msg + } + + return objectMatchesStructure(msg, { wasm: { execute: { contract_addr: {}, @@ -89,7 +103,9 @@ export const makeDeletePostActionMaker = ({ }, }, }, - }) && msg.wasm.execute.contract_addr === contract + }) && + chainId === pressChainId && + msg.wasm.execute.contract_addr === contract ? { match: true, data: { @@ -99,23 +115,28 @@ export const makeDeletePostActionMaker = ({ : { match: false, } + } const useTransformToCosmos: UseTransformToCosmos = () => useCallback( ({ id }) => - makeWasmMessage({ - wasm: { - execute: { - contract_addr: contract, - funds: [], - msg: { - burn: { - token_id: id, + maybeMakePolytoneExecuteMessage( + daoChainId, + pressChainId, + makeWasmMessage({ + wasm: { + execute: { + contract_addr: contract, + funds: [], + msg: { + burn: { + token_id: id, + }, }, }, }, - }, - }), + }) + ), [] ) diff --git a/packages/stateful/widgets/widgets/Press/actions/UpdatePost/index.tsx b/packages/stateful/widgets/widgets/Press/actions/UpdatePost/index.tsx index dcdc5152f..76d588849 100644 --- a/packages/stateful/widgets/widgets/Press/actions/UpdatePost/index.tsx +++ b/packages/stateful/widgets/widgets/Press/actions/UpdatePost/index.tsx @@ -4,14 +4,19 @@ import { useFormContext } from 'react-hook-form' import { PencilEmoji, useCachedLoading } from '@dao-dao/stateless' import { ActionComponent, - ActionContextType, ActionKey, ActionMaker, UseDecodedCosmosMsg, UseDefaults, UseTransformToCosmos, } from '@dao-dao/types/actions' -import { makeWasmMessage, objectMatchesStructure } from '@dao-dao/utils' +import { + decodePolytoneExecuteMsg, + getChainAddressForActionOptions, + makeWasmMessage, + maybeMakePolytoneExecuteMessage, + objectMatchesStructure, +} from '@dao-dao/utils' import { useActionOptions } from '../../../../../actions' import { postSelector, postsSelector } from '../../state' @@ -30,14 +35,18 @@ const useDefaults: UseDefaults = () => ({ }) export const makeUpdatePostActionMaker = ({ + chainId: configuredChainId, contract, }: PressData): ActionMaker => { // Make outside of the maker function returned below so it doesn't get // redefined and thus remounted on every render. const Component: ActionComponent = (props) => { const { - chain: { chain_id: chainId }, + chain: { chain_id: daoChainId }, } = useActionOptions() + // The chain that Press is set up on. If chain ID is undefined, default to + // native DAO chain for backwards compatibility. + const pressChainId = configuredChainId || daoChainId const { watch } = useFormContext() const tokenId = watch((props.fieldNamePrefix + 'tokenId') as 'tokenId') @@ -57,7 +66,7 @@ export const makeUpdatePostActionMaker = ({ const postsLoading = useCachedLoading( postsSelector({ contractAddress: contract, - chainId, + chainId: pressChainId, }), [] ) @@ -73,16 +82,27 @@ export const makeUpdatePostActionMaker = ({ ) } - return ({ t, context, address }) => { - // Only available in DAO context. - if (context.type !== ActionContextType.Dao) { - return null - } + return (options) => { + const { + t, + chain: { chain_id: daoChainId }, + } = options + + // The chain that Press is set up on. If chain ID is undefined, default to + // native DAO chain for backwards compatibility. + const pressChainId = configuredChainId || daoChainId const useDecodedCosmosMsg: UseDecodedCosmosMsg = ( msg: Record - ) => - objectMatchesStructure(msg, { + ) => { + let chainId = daoChainId + const decodedPolytone = decodePolytoneExecuteMsg(chainId, msg) + if (decodedPolytone.match) { + chainId = decodedPolytone.chainId + msg = decodedPolytone.msg + } + + return objectMatchesStructure(msg, { wasm: { execute: { contract_addr: {}, @@ -97,11 +117,13 @@ export const makeUpdatePostActionMaker = ({ }, }, }) && - msg.wasm.execute.contract_addr === contract && - msg.wasm.execute.msg.mint.token_uri + chainId === pressChainId && + msg.wasm.execute.contract_addr === contract && + msg.wasm.execute.msg.mint.token_uri ? { match: true, data: { + chainId, tokenId: msg.wasm.execute.msg.mint.token_id, tokenUri: msg.wasm.execute.msg.mint.token_uri, uploaded: true, @@ -110,25 +132,33 @@ export const makeUpdatePostActionMaker = ({ : { match: false, } + } const useTransformToCosmos: UseTransformToCosmos = () => useCallback( ({ tokenId, tokenUri }) => - makeWasmMessage({ - wasm: { - execute: { - contract_addr: contract, - funds: [], - msg: { - mint: { - owner: address, - token_id: tokenId, - token_uri: tokenUri, + maybeMakePolytoneExecuteMessage( + daoChainId, + pressChainId, + makeWasmMessage({ + wasm: { + execute: { + contract_addr: contract, + funds: [], + msg: { + mint: { + owner: getChainAddressForActionOptions( + options, + pressChainId + ), + token_id: tokenId, + token_uri: tokenUri, + }, }, }, }, - }, - }), + }) + ), [] ) diff --git a/packages/stateful/widgets/widgets/Press/types.ts b/packages/stateful/widgets/widgets/Press/types.ts index cc6dcc093..02b28ea83 100644 --- a/packages/stateful/widgets/widgets/Press/types.ts +++ b/packages/stateful/widgets/widgets/Press/types.ts @@ -1,4 +1,8 @@ export type PressData = { + // Which chain the contract exists on. If undefined, the chain is the same as + // the DAO's native chain. This fallback ensures backwards compatibility with + // presses that were setup before polytone accounts existed. + chainId?: string // NFT collection contract address. contract: string } diff --git a/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx b/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx index 83ac39a83..3b10fd3b1 100644 --- a/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx +++ b/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx @@ -257,8 +257,6 @@ export const WyndDepositRenderer = ({ return } - const signingCosmWasmClient = await getSigningCosmWasmClient() - setDepositing(true) setError('') setTxHash('') @@ -266,6 +264,7 @@ export const WyndDepositRenderer = ({ try { let tx + const signingCosmWasmClient = await getSigningCosmWasmClient() if (inTokenIsOutToken) { if (token.type === TokenType.Native) { tx = await signingCosmWasmClient.sendTokens( diff --git a/packages/stateful/widgets/widgets/WyndDeposit/index.ts b/packages/stateful/widgets/widgets/WyndDeposit/index.ts index 42beabed3..adc41872b 100644 --- a/packages/stateful/widgets/widgets/WyndDeposit/index.ts +++ b/packages/stateful/widgets/widgets/WyndDeposit/index.ts @@ -15,6 +15,8 @@ export const WyndDepositWidget: Widget = { id: 'wynd_deposit', location: WidgetLocation.Home, visibilityContext: WidgetVisibilityContext.Always, + // WYND only available on Juno mainnet. + supportedChainIds: [ChainId.JunoMainnet], defaultValues: { outputToken: { type: TokenType.Native, diff --git a/packages/stateless/components/buttons/Buttonifier.tsx b/packages/stateless/components/buttons/Buttonifier.tsx index c444bf64b..9e9b8aa7e 100644 --- a/packages/stateless/components/buttons/Buttonifier.tsx +++ b/packages/stateless/components/buttons/Buttonifier.tsx @@ -5,7 +5,7 @@ import { ButtonifierProps } from '@dao-dao/types' import { Loader } from '../logo' const defaultVariant = 'primary' -const defaultSize = 'default' +const defaultSize = 'md' // Pulse for these variants instead of displaying loader. const PULSE_LOADING_VARIANTS = 'underline' || 'none' @@ -66,7 +66,7 @@ export const getButtonifiedClassNames = ({ // Sizes. 'button-text py-[10px] px-[14px]': size === 'lg', 'button-text-sm py-1 px-2': size === 'sm', - 'link-text py-[6px] px-[10px]': size === 'default', + 'link-text py-[6px] px-[10px]': size === 'md', }, // Primary variant diff --git a/packages/stateless/components/inputs/ChainPickerInput.tsx b/packages/stateless/components/inputs/ChainPickerInput.tsx index 8411d436c..28a4fcf51 100644 --- a/packages/stateless/components/inputs/ChainPickerInput.tsx +++ b/packages/stateless/components/inputs/ChainPickerInput.tsx @@ -5,7 +5,10 @@ import { getNativeTokenForChainId, } from '@dao-dao/utils' -import { useSupportedChainContext } from '../../hooks' +import { + useDaoInfoContextIfAvailable, + useSupportedChainContext, +} from '../../hooks' import { RadioInput } from './RadioInput' export type ChainPickerInputProps = { @@ -16,6 +19,10 @@ export type ChainPickerInputProps = { disabled?: boolean onChange?: (chainId: string) => void excludeChainIds?: string[] + // Whether to include only the chains the DAO has an account on (its native + // chain or one of its polytone chains). + // Defaults to false. + onlyDaoChainIds?: boolean className?: string } @@ -25,6 +32,7 @@ export const ChainPickerInput = ({ disabled, onChange, excludeChainIds, + onlyDaoChainIds = false, className, }: ChainPickerInputProps) => { const { @@ -32,6 +40,12 @@ export const ChainPickerInput = ({ config: { polytone }, } = useSupportedChainContext() const { watch, setValue } = useFormContext() + const daoInfo = useDaoInfoContextIfAvailable() + + const includeChainIds = + onlyDaoChainIds && daoInfo + ? [daoInfo.chainId, ...Object.keys(daoInfo.polytoneProxies)] + : undefined const polytoneChains = Object.keys(polytone || {}) @@ -50,7 +64,11 @@ export const ChainPickerInput = ({ // Other chains with Polytone. ...polytoneChains, ] - .filter((chainId) => !excludeChainIds?.includes(chainId)) + .filter( + (chainId) => + (!includeChainIds || includeChainIds.includes(chainId)) && + !excludeChainIds?.includes(chainId) + ) .map((chainId) => ({ label: labelMode === 'chain' diff --git a/packages/stateless/components/wallet/ConnectWallet.tsx b/packages/stateless/components/wallet/ConnectWallet.tsx index 0b72f5779..13451b35d 100644 --- a/packages/stateless/components/wallet/ConnectWallet.tsx +++ b/packages/stateless/components/wallet/ConnectWallet.tsx @@ -1,4 +1,5 @@ import { Sensors } from '@mui/icons-material' +import clsx from 'clsx' import { forwardRef } from 'react' import { useTranslation } from 'react-i18next' @@ -7,18 +8,24 @@ import { ButtonProps } from '@dao-dao/types' import { Button } from '../buttons' export interface ConnectWalletProps - extends Partial> { + extends Partial> { onConnect?: () => void className?: string } export const ConnectWallet = forwardRef( - function ConnectWallet({ onConnect, ...props }, ref) { + function ConnectWallet({ onConnect, size = 'lg', ...props }, ref) { const { t } = useTranslation() return ( - ) diff --git a/packages/types/stateless/Buttonifier.tsx b/packages/types/stateless/Buttonifier.tsx index 5f28c2faf..c998cf6a1 100644 --- a/packages/types/stateless/Buttonifier.tsx +++ b/packages/types/stateless/Buttonifier.tsx @@ -11,7 +11,7 @@ export interface ButtonifierProps { | 'ghost_outline' | 'underline' | 'none' - size?: 'sm' | 'lg' | 'default' | 'none' + size?: 'sm' | 'lg' | 'md' | 'none' loading?: boolean contentContainerClassName?: string pressed?: boolean diff --git a/packages/types/widgets.ts b/packages/types/widgets.ts index 443eb6f6e..7564deaaa 100644 --- a/packages/types/widgets.ts +++ b/packages/types/widgets.ts @@ -35,6 +35,8 @@ export type Widget = any> = { location: WidgetLocation // The context in which the widget is visible. visibilityContext: WidgetVisibilityContext + // If defined, the widget is only available on these chains. + supportedChainIds?: string[] // The default values for the widget's variables. defaultValues?: Variables // Component that renders the widget. diff --git a/packages/utils/actions.ts b/packages/utils/actions.ts index 8714d1bd4..e21c136fd 100644 --- a/packages/utils/actions.ts +++ b/packages/utils/actions.ts @@ -65,4 +65,4 @@ export const getChainAddressForActionOptions = ( : // If on different chain, return wallet's transformed bech32 address. context.type === ActionContextType.Wallet ? transformBech32Address(address, chainId) - : '' + : undefined From d5f95b935a863dbefe808df80aa0d26ae774a48d Mon Sep 17 00:00:00 2001 From: noah Date: Thu, 9 Nov 2023 08:23:33 -0800 Subject: [PATCH 4/4] Fixed transfer NFT with smart contract msg action. (#1460) --- .../stateful/actions/core/nfts/TransferNft/index.tsx | 10 ++++++---- packages/utils/contracts.ts | 5 ++--- packages/utils/messages/cw.ts | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/stateful/actions/core/nfts/TransferNft/index.tsx b/packages/stateful/actions/core/nfts/TransferNft/index.tsx index 0668eb126..fa44614da 100644 --- a/packages/stateful/actions/core/nfts/TransferNft/index.tsx +++ b/packages/stateful/actions/core/nfts/TransferNft/index.tsx @@ -1,4 +1,3 @@ -import { toBase64, toUtf8 } from '@cosmjs/encoding' import { useCallback } from 'react' import { useFormContext } from 'react-hook-form' import { constSelector, useRecoilValue } from 'recoil' @@ -18,6 +17,7 @@ import { import { combineLoadingDataWithErrors, decodePolytoneExecuteMsg, + encodeMessageAsBase64, makeWasmMessage, maybeMakePolytoneExecuteMessage, objectMatchesStructure, @@ -78,7 +78,7 @@ const useTransformToCosmos: UseTransformToCosmos = () => { ? { send_nft: { contract: recipient, - msg: toBase64(toUtf8(JSON.stringify(smartContractMsg))), + msg: encodeMessageAsBase64(smartContractMsg), token_id: tokenId, }, } @@ -156,8 +156,10 @@ const useDecodedCosmosMsg: UseDecodedCosmosMsg = ( recipient: msg.wasm.execute.msg.send_nft.contract, executeSmartContract: true, - smartContractMsg: parseEncodedMessage( - msg.wasm.execute.msg.send_nft.msg + smartContractMsg: JSON.stringify( + parseEncodedMessage(msg.wasm.execute.msg.send_nft.msg), + null, + 2 ), }, } diff --git a/packages/utils/contracts.ts b/packages/utils/contracts.ts index 28017b576..4a3dc6214 100644 --- a/packages/utils/contracts.ts +++ b/packages/utils/contracts.ts @@ -1,12 +1,11 @@ import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' -import { toBase64, toUtf8 } from '@cosmjs/encoding' import { Coin, SigningStargateClient } from '@cosmjs/stargate' import { findAttribute, parseRawLog } from '@cosmjs/stargate/build/logs' import { ContractVersion } from '@dao-dao/types' import { CHAIN_GAS_MULTIPLIER } from './constants' -import { cwMsgToEncodeObject } from './messages' +import { cwMsgToEncodeObject, encodeMessageAsBase64 } from './messages' const CONTRACT_VERSIONS = Object.values(ContractVersion) @@ -55,7 +54,7 @@ export const instantiateSmartContract = async ( wasm: { instantiate: { code_id: codeId, - msg: toBase64(toUtf8(JSON.stringify(msg))), + msg: encodeMessageAsBase64(msg), funds: funds || [], label, // Replace empty string with undefined. diff --git a/packages/utils/messages/cw.ts b/packages/utils/messages/cw.ts index c97bf33a6..ceb545d8b 100644 --- a/packages/utils/messages/cw.ts +++ b/packages/utils/messages/cw.ts @@ -15,7 +15,7 @@ import { import { getSupportedChainConfig } from '../chain' import { POLYTONE_TIMEOUT_SECONDS } from '../constants' import { objectMatchesStructure } from '../objectMatchesStructure' -import { parseEncodedMessage } from './encoding' +import { encodeMessageAsBase64, parseEncodedMessage } from './encoding' import { decodeStargateMessage } from './protobuf' type WasmMsgType = @@ -147,7 +147,7 @@ export const makeExecutableMintMessage = ( wasm: { execute: { contract_addr: contractAddress, - msg: toBase64(toUtf8(JSON.stringify(msg))), + msg: encodeMessageAsBase64(msg), funds: [], }, },