diff --git a/apps/dapp/pages/gov/[chain]/[[...slug]].tsx b/apps/dapp/pages/gov/[chain]/[[...slug]].tsx index 85ce324ac..4d674a606 100644 --- a/apps/dapp/pages/gov/[chain]/[[...slug]].tsx +++ b/apps/dapp/pages/gov/[chain]/[[...slug]].tsx @@ -32,21 +32,24 @@ import { DaoDappTabbedHome, GovernanceHome, useChain, + useConfiguredChainContext, useDaoInfoContext, - useSupportedChainContext, } from '@dao-dao/stateless' import { ChainId, DaoTabId, DaoTabWithComponent } from '@dao-dao/types' import { NEUTRON_GOVERNANCE_DAO, SITE_URL, + getConfiguredChainConfig, + getConfiguredChains, getGovPath, - getSupportedChainConfig, - getSupportedChains, } from '@dao-dao/utils' const InnerGovHome = () => { const { t } = useTranslation() - const { chainId, config } = useSupportedChainContext() + const { + chainId, + config: { name }, + } = useConfiguredChainContext() const daoInfo = useDaoInfoContext() const router = useRouter() @@ -70,19 +73,19 @@ const InnerGovHome = () => { // Pre-fetch tabs. useEffect(() => { tabs.forEach((tab) => { - router.prefetch(getGovPath(config.name, tab.id)) + router.prefetch(getGovPath(name, tab.id)) }) - }, [config.name, router, tabs]) + }, [name, router, tabs]) const slug = (router.query.slug || []) as string[] useEffect(() => { // If no slug, redirect to first tab. if (slug.length === 0) { - router.push(getGovPath(config.name, firstTabId), undefined, { + router.push(getGovPath(name, firstTabId), undefined, { shallow: true, }) } - }, [router, slug.length, firstTabId, config.name]) + }, [router, slug.length, firstTabId, name]) const tabId = slug.length > 0 && tabs.some(({ id }) => id === slug[0]) @@ -90,14 +93,14 @@ const InnerGovHome = () => { : // If tab is invalid, default to first tab. firstTabId const onSelectTabId = (tabId: string) => - router.push(getGovPath(config.name, tabId), undefined, { + router.push(getGovPath(name, tabId), undefined, { shallow: true, }) const [goingToChainId, setGoingToChainId] = useState() // Pre-fetch other chains. useEffect(() => { - getSupportedChains().forEach(({ name }) => { + getConfiguredChains().forEach(({ name }) => { router.prefetch(getGovPath(name)) tabs.map((tab) => router.prefetch(getGovPath(name, tab.id))) }) @@ -113,13 +116,11 @@ const InnerGovHome = () => { loading={!!goingToChainId && goingToChainId !== chainId} onSelect={(chainId) => { router.push( - getGovPath( - getSupportedChainConfig(chainId)?.name || config.name, - tabId - ) + getGovPath(getConfiguredChainConfig(chainId)?.name || name, tabId) ) setGoingToChainId(chainId) }} + type="configured" /> } daoInfo={daoInfo} @@ -163,7 +164,7 @@ const NeutronGovHome: NextPage = () => { const [goingToChainId, setGoingToChainId] = useState() // Pre-fetch other chains. useEffect(() => { - getSupportedChains().forEach(({ name }) => { + getConfiguredChains().forEach(({ name }) => { router.prefetch('/' + name) }) }, [router]) @@ -175,12 +176,13 @@ const NeutronGovHome: NextPage = () => { { - const chainConfig = getSupportedChainConfig(chainId) + const chainConfig = getConfiguredChainConfig(chainId) if (chainConfig) { router.push(getGovPath(chainConfig.name)) setGoingToChainId(chainId) } }} + type="configured" /> } daos={daosLoading} diff --git a/apps/dapp/pages/gov/index.tsx b/apps/dapp/pages/gov/index.tsx index a155e18ce..dde2ec20d 100644 --- a/apps/dapp/pages/gov/index.tsx +++ b/apps/dapp/pages/gov/index.tsx @@ -7,14 +7,22 @@ import { useRecoilValue } from 'recoil' import { walletChainIdAtom } from '@dao-dao/state' import { PageLoader } from '@dao-dao/stateless' -import { getGovPath, getSupportedChainConfig } from '@dao-dao/utils' +import { + getConfiguredChainConfig, + getConfiguredChains, + getGovPath, +} from '@dao-dao/utils' const GovRedirectPage: NextPage = () => { const chainId = useRecoilValue(walletChainIdAtom) const router = useRouter() useEffect(() => { - router.push(getGovPath(getSupportedChainConfig(chainId)!.name)) + router.push( + getGovPath( + getConfiguredChainConfig(chainId)?.name || getConfiguredChains()[0].name + ) + ) }, [chainId, router]) return diff --git a/packages/state/recoil/atoms/chain.ts b/packages/state/recoil/atoms/chain.ts index 82098f47d..93f587e70 100644 --- a/packages/state/recoil/atoms/chain.ts +++ b/packages/state/recoil/atoms/chain.ts @@ -1,7 +1,7 @@ import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' import { atom, atomFamily } from 'recoil' -import { DEFAULT_CHAIN_ID, getSupportedChains } from '@dao-dao/utils' +import { DEFAULT_CHAIN_ID, getConfiguredChains } from '@dao-dao/utils' import { localStorageEffect } from '../effects' @@ -20,7 +20,7 @@ export const walletChainIdAtom = atom({ localStorageEffect(JSON.stringify, (jsonValue: string) => { const value = JSON.parse(jsonValue) // If no supported chain matches, set to default. - return getSupportedChains().some(({ chain }) => chain.chain_id === value) + return getConfiguredChains().some(({ chain }) => chain.chain_id === value) ? value : DEFAULT_CHAIN_ID }), diff --git a/packages/stateful/actions/core/chain_governance/GovernanceProposal/Component.tsx b/packages/stateful/actions/core/chain_governance/GovernanceProposal/Component.tsx index cbb8659d3..f85b54fc7 100644 --- a/packages/stateful/actions/core/chain_governance/GovernanceProposal/Component.tsx +++ b/packages/stateful/actions/core/chain_governance/GovernanceProposal/Component.tsx @@ -20,7 +20,7 @@ import { TextAreaInput, TextInput, TokenInput, - useSupportedChainContext, + useConfiguredChainContext, } from '@dao-dao/stateless' import { AddressInputProps, @@ -91,9 +91,11 @@ export const GovernanceProposalComponent: ActionComponent< const { chainId, chain: { bech32_prefix: bech32Prefix }, - config: { supportsV1GovProposals }, + config: { + gov: { supportsV1GovProposals }, + }, nativeToken, - } = useSupportedChainContext() + } = useConfiguredChainContext() const selectedMinDepositToken = minDeposits.loading ? undefined diff --git a/packages/stateful/actions/core/chain_governance/GovernanceProposal/index.tsx b/packages/stateful/actions/core/chain_governance/GovernanceProposal/index.tsx index 800069be6..a7f117634 100644 --- a/packages/stateful/actions/core/chain_governance/GovernanceProposal/index.tsx +++ b/packages/stateful/actions/core/chain_governance/GovernanceProposal/index.tsx @@ -213,7 +213,9 @@ export const makeGovernanceProposalAction: ActionMaker< context, chain: { chain_id: currentChainId }, chainContext: { - config: { supportsV1GovProposals }, + config: { + gov: { supportsV1GovProposals }, + }, }, }) => { const useDefaults: UseDefaults = () => { diff --git a/packages/stateful/actions/core/dao_appearance/index.ts b/packages/stateful/actions/core/dao_appearance/index.ts index 445dcf9cc..e6e630609 100644 --- a/packages/stateful/actions/core/dao_appearance/index.ts +++ b/packages/stateful/actions/core/dao_appearance/index.ts @@ -1,13 +1,22 @@ -import { ActionCategoryKey, ActionCategoryMaker } from '@dao-dao/types' +import { + ActionCategoryKey, + ActionCategoryMaker, + ActionContextType, +} from '@dao-dao/types' import { makeManageWidgetsAction } from './ManageWidgets' import { makeUpdateInfoAction } from './UpdateInfo' export const makeDaoAppearanceActionCategory: ActionCategoryMaker = ({ t, -}) => ({ - key: ActionCategoryKey.DaoAppearance, - label: t('actionCategory.daoAppearanceLabel'), - description: t('actionCategory.daoAppearanceDescription'), - actionMakers: [makeUpdateInfoAction, makeManageWidgetsAction], -}) + context, +}) => + // Only DAOs. + context.type === ActionContextType.Dao + ? { + key: ActionCategoryKey.DaoAppearance, + label: t('actionCategory.daoAppearanceLabel'), + description: t('actionCategory.daoAppearanceDescription'), + actionMakers: [makeUpdateInfoAction, makeManageWidgetsAction], + } + : null diff --git a/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/Component.tsx b/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/Component.tsx index 27a76bba1..30f6af7e7 100644 --- a/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/Component.tsx +++ b/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/Component.tsx @@ -2,7 +2,11 @@ import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { CopyToClipboard, RadioInput } from '@dao-dao/stateless' -import { ActionComponent, ActionContextType } from '@dao-dao/types/actions' +import { + ActionChainContextType, + ActionComponent, + ActionContextType, +} from '@dao-dao/types/actions' import { getDisplayNameForChainId, getImageUrlForChainId } from '@dao-dao/utils' import { useActionOptions } from '../../../react' @@ -26,6 +30,7 @@ export const CreateCrossChainAccountComponent: ActionComponent = ({ if ( context.type !== ActionContextType.Dao || // Type check. + chainContext.type !== ActionChainContextType.Supported || !chainContext.config.polytone ) { throw new Error('Invalid context for this action.') diff --git a/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/index.tsx b/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/index.tsx index 298cc1587..90aeed865 100644 --- a/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/index.tsx +++ b/packages/stateful/actions/core/dao_governance/CreateCrossChainAccount/index.tsx @@ -2,6 +2,7 @@ import { useCallback } from 'react' import { ChainEmoji } from '@dao-dao/stateless' import { + ActionChainContextType, ActionContextType, ActionKey, ActionMaker, @@ -21,22 +22,18 @@ import { export const makeCreateCrossChainAccountAction: ActionMaker< CreateCrossChainAccountData -> = ({ - t, - context, - chain, - chainContext: { - config: { polytone }, - }, -}) => { +> = ({ t, context, chain, chainContext }) => { // Only allow using this action in DAOs. - if (context.type !== ActionContextType.Dao) { + if ( + context.type !== ActionContextType.Dao || + chainContext.type !== ActionChainContextType.Supported + ) { return null } - const missingChainIds = Object.keys(polytone || {}).filter( - (chainId) => !(chainId in context.info.polytoneProxies) - ) + const missingChainIds = Object.keys( + chainContext.config.polytone || {} + ).filter((chainId) => !(chainId in context.info.polytoneProxies)) const useDefaults: UseDefaults = () => ({ chainId: missingChainIds[0], diff --git a/packages/stateful/actions/core/dao_governance/DaoAdminExec/index.tsx b/packages/stateful/actions/core/dao_governance/DaoAdminExec/index.tsx index d6067e9cc..abf9fe83c 100644 --- a/packages/stateful/actions/core/dao_governance/DaoAdminExec/index.tsx +++ b/packages/stateful/actions/core/dao_governance/DaoAdminExec/index.tsx @@ -218,10 +218,13 @@ const useDecodedCosmosMsg: UseDecodedCosmosMsg = ( export const makeDaoAdminExecAction: ActionMaker = ({ t, context, + chainContext: { + config: { noCosmWasm }, + }, }) => - // Only allow using this action in DAOs or gov props. + // Only allow using this action in DAOs or gov props on chains with CW. context.type === ActionContextType.Dao || - context.type === ActionContextType.Gov + (context.type === ActionContextType.Gov && !noCosmWasm) ? { key: ActionKey.DaoAdminExec, Icon: JoystickEmoji, diff --git a/packages/stateful/actions/core/dao_governance/EnableMultipleChoice/index.tsx b/packages/stateful/actions/core/dao_governance/EnableMultipleChoice/index.tsx index eba4d383e..86d086248 100644 --- a/packages/stateful/actions/core/dao_governance/EnableMultipleChoice/index.tsx +++ b/packages/stateful/actions/core/dao_governance/EnableMultipleChoice/index.tsx @@ -10,6 +10,7 @@ import { TokenType, } from '@dao-dao/types' import { + ActionChainContextType, ActionContextType, ActionKey, ActionMaker, @@ -81,16 +82,15 @@ const useDecodedCosmosMsg: UseDecodedCosmosMsg = ( export const makeEnableMultipleChoiceAction: ActionMaker< EnableMultipleChoiceData -> = ({ - t, - address, - context, - chain: { chain_id: chainId }, - chainContext: { config: chainConfig }, -}) => { +> = ({ t, address, context, chain: { chain_id: chainId }, chainContext }) => { + // Disallows creation if multiple choice proposal module already exists, down + // at the bottom of this function, instead of returning null here. This + // ensures this action can be rendered correctly in past proposals after + // multiple choice is enabled. if ( context.type !== ActionContextType.Dao || - !context.info.supportedFeatures[Feature.MultipleChoiceProposals] + !context.info.supportedFeatures[Feature.MultipleChoiceProposals] || + chainContext.type !== ActionChainContextType.Supported ) { return null } @@ -152,7 +152,7 @@ export const makeEnableMultipleChoiceAction: ActionMaker< } const info = DaoProposalMultipleAdapter.daoCreation.getInstantiateInfo( - chainConfig, + chainContext.config, { ...makeDefaultNewDao(chainId), // Only the name is used in this function to pick the contract label. diff --git a/packages/stateful/actions/core/dao_governance/UpgradeV1ToV2/index.tsx b/packages/stateful/actions/core/dao_governance/UpgradeV1ToV2/index.tsx index 2951c4468..e217b5939 100644 --- a/packages/stateful/actions/core/dao_governance/UpgradeV1ToV2/index.tsx +++ b/packages/stateful/actions/core/dao_governance/UpgradeV1ToV2/index.tsx @@ -13,6 +13,7 @@ import { useCachedLoading, } from '@dao-dao/stateless' import { + ActionChainContextType, ActionComponent, ActionContextType, ActionKey, @@ -139,18 +140,19 @@ export const makeUpgradeV1ToV2Action: ActionMaker = ({ t, address, chain, - chainContext: { - config: { codeIds }, - }, + chainContext, }) => { if ( context.type !== ActionContextType.Dao || // If no DAO migrator, don't show upgrade action. - codeIds.DaoMigrator <= 0 + chainContext.type !== ActionChainContextType.Supported || + chainContext.config.codeIds.DaoMigrator <= 0 ) { return null } + const { codeIds } = chainContext.config + const useDefaults: UseDefaults = () => { // Load sub DAOs for registering as the current DAO upgrades to v2. If this // DAO is not on v1, there are no SubDAOs to load. diff --git a/packages/stateful/actions/core/nfts/index.ts b/packages/stateful/actions/core/nfts/index.ts index 8ddf603a9..73e2e9f70 100644 --- a/packages/stateful/actions/core/nfts/index.ts +++ b/packages/stateful/actions/core/nfts/index.ts @@ -9,17 +9,24 @@ import { makeTransferNftAction } from './TransferNft' export const makeManageNftsActionCategory: ActionCategoryMaker = ({ t, context, -}) => ({ - key: ActionCategoryKey.Nfts, - label: t('actionCategory.nftsLabel'), - description: t('actionCategory.nftsDescription', { - context: context.type, - }), - actionMakers: [ - makeCreateNftCollectionAction, - makeMintNftAction, - makeTransferNftAction, - makeBurnNftAction, - makeManageCw721Action, - ], -}) + chainContext: { + config: { noCosmWasm }, + }, +}) => + // Chains without CosmWasm cannot use NFTs. + !noCosmWasm + ? { + key: ActionCategoryKey.Nfts, + label: t('actionCategory.nftsLabel'), + description: t('actionCategory.nftsDescription', { + context: context.type, + }), + actionMakers: [ + makeCreateNftCollectionAction, + makeMintNftAction, + makeTransferNftAction, + makeBurnNftAction, + makeManageCw721Action, + ], + } + : null diff --git a/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx b/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx index dcf4a8d24..5d5a43a33 100644 --- a/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx +++ b/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx @@ -7,6 +7,7 @@ import { genericTokenSelector } from '@dao-dao/state/recoil' import { DownArrowEmoji } from '@dao-dao/stateless' import { ChainId, TokenType, UseDecodedCosmosMsg } from '@dao-dao/types' import { + ActionChainContextType, ActionComponent, ActionKey, ActionMaker, @@ -66,21 +67,19 @@ const Component: ActionComponent = ( export const makeCommunityPoolDepositAction: ActionMaker< CommunityPoolDepositData -> = ({ - t, - address, - chain: { chain_id: currentChainId }, - chainContext: { nativeToken }, -}) => { +> = ({ t, address, chain: { chain_id: currentChainId }, chainContext }) => { // Neutron does not use the x/distribution community pool. - if (currentChainId === ChainId.NeutronMainnet) { + if ( + currentChainId === ChainId.NeutronMainnet || + chainContext.type !== ActionChainContextType.Supported + ) { return null } const useDefaults: UseDefaults = () => ({ chainId: currentChainId, amount: 100, - denom: nativeToken?.denomOrAddress || '', + denom: chainContext.nativeToken?.denomOrAddress || '', }) const useTransformToCosmos: UseTransformToCosmos< 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 0fb59330f..90c3ce682 100644 --- a/packages/stateful/actions/core/treasury/token_swap/stateful/InstantiateTokenSwap.tsx +++ b/packages/stateful/actions/core/treasury/token_swap/stateful/InstantiateTokenSwap.tsx @@ -7,7 +7,11 @@ import { constSelector, useRecoilValueLoadable } from 'recoil' import { genericTokenBalancesSelector } from '@dao-dao/state' import { DaoCoreV2Selectors } from '@dao-dao/state/recoil' import { Loader, useCachedLoading } from '@dao-dao/stateless' -import { ActionComponent, TokenType } from '@dao-dao/types' +import { + ActionChainContextType, + ActionComponent, + TokenType, +} from '@dao-dao/types' import { InstantiateMsg } from '@dao-dao/types/contracts/CwTokenSwap' import { convertDenomToMicroDenomWithDecimals, @@ -30,12 +34,15 @@ export const InstantiateTokenSwap: ActionComponent< PerformTokenSwapData > = (props) => { const { t } = useTranslation() - const { - address: selfAddress, - chainContext: { - config: { codeIds }, - }, - } = useActionOptions() + const { address: selfAddress, chainContext } = useActionOptions() + + const cwTokenSwapCodeId = + chainContext.type === ActionChainContextType.Supported + ? chainContext.config.codeIds?.CwTokenSwap + : undefined + if (!cwTokenSwapCodeId) { + throw new Error('Unsupported chain.') + } const { setValue } = useFormContext() const { address: walletAddress, getSigningCosmWasmClient } = useWallet() @@ -111,7 +118,7 @@ export const InstantiateTokenSwap: ActionComponent< const contractAddress = await instantiateSmartContract( signingCosmWasmClient, walletAddress, - codeIds.CwTokenSwap, + cwTokenSwapCodeId, 'Token Swap', instantiateMsg ) @@ -137,7 +144,7 @@ export const InstantiateTokenSwap: ActionComponent< setInstantiating(false) } }, [ - codeIds.CwTokenSwap, + cwTokenSwapCodeId, props.data, props.fieldNamePrefix, selfAddress, diff --git a/packages/stateful/actions/react/provider.tsx b/packages/stateful/actions/react/provider.tsx index 93cca450e..e5d468b9b 100644 --- a/packages/stateful/actions/react/provider.tsx +++ b/packages/stateful/actions/react/provider.tsx @@ -6,10 +6,13 @@ import { govParamsSelector, moduleAddressSelector } from '@dao-dao/state/recoil' import { Loader, useChain, + useChainContext, useDaoInfoContext, useSupportedChainContext, } from '@dao-dao/stateless' import { + ActionChainContext, + ActionChainContextType, ActionContext, ActionContextType, ActionOptions, @@ -48,7 +51,11 @@ export const DaoActionsProvider = ({ children }: ActionsProviderProps) => { const options: ActionOptions = { t, chain: chainContext.chain, - chainContext, + chainContext: { + type: ActionChainContextType.Supported, + ...chainContext, + ...chainContext.config, + }, address: info.coreAddress, context: { type: ActionContextType.Dao, @@ -136,11 +143,28 @@ export const BaseActionsProvider = ({ }) => { const { t } = useTranslation() - const chainContext = useSupportedChainContext() + const chainContext = useChainContext() + const actionChainContext: ActionChainContext | undefined = chainContext.base + ? { + type: ActionChainContextType.Base, + ...chainContext, + config: chainContext.base, + } + : chainContext.config + ? { + type: ActionChainContextType.Supported, + ...chainContext, + config: chainContext.config, + } + : undefined + if (!actionChainContext) { + throw new Error('Invalid chain context') + } + const options: ActionOptions = { t, chain: chainContext.chain, - chainContext, + chainContext: actionChainContext, address, context, } diff --git a/packages/stateful/components/ChainSwitcher.tsx b/packages/stateful/components/ChainSwitcher.tsx index 244788caf..cf663a46d 100644 --- a/packages/stateful/components/ChainSwitcher.tsx +++ b/packages/stateful/components/ChainSwitcher.tsx @@ -1,9 +1,12 @@ import { useRecoilState } from 'recoil' import { walletChainIdAtom } from '@dao-dao/state/recoil' -import { ChainSwitcher as StatelessChainSwitcher } from '@dao-dao/stateless' +import { + ChainSwitcher as StatelessChainSwitcher, + ChainSwitcherProps as StatelessChainSwitcherProps, +} from '@dao-dao/stateless' -export type ChainSwitcherProps = { +export type ChainSwitcherProps = Pick & { chainId?: string onSelect?: (chainId: string) => void loading?: boolean @@ -15,11 +18,13 @@ export const ChainSwitcher = ({ onSelect, loading, excludeChainIds, + ...props }: ChainSwitcherProps) => { const [chainId, setChainId] = useRecoilState(walletChainIdAtom) return ( @@ -30,3 +35,7 @@ export const ChainSwitcher = ({ /> ) } + +export const AllConfiguredChainSwitcher = ( + props: Omit +) => diff --git a/packages/stateful/components/dao/CreateDaoForm.tsx b/packages/stateful/components/dao/CreateDaoForm.tsx index 97deef6cd..436083e7e 100644 --- a/packages/stateful/components/dao/CreateDaoForm.tsx +++ b/packages/stateful/components/dao/CreateDaoForm.tsx @@ -7,7 +7,12 @@ import { useEffect, useMemo, useState } from 'react' import { SubmitErrorHandler, SubmitHandler, useForm } from 'react-hook-form' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' -import { constSelector, useRecoilState, useRecoilValue } from 'recoil' +import { + constSelector, + useRecoilState, + useRecoilValue, + useSetRecoilState, +} from 'recoil' import { averageColorSelector, walletChainIdAtom } from '@dao-dao/state/recoil' import { @@ -17,6 +22,7 @@ import { DaoCreateSidebarCard, DaoHeader, ImageSelector, + Loader, PageHeaderContent, RightSidebarContent, TooltipInfoIcon, @@ -48,6 +54,7 @@ import { getFundsFromDaoInstantiateMsg, getNativeTokenForChainId, getSupportedChainConfig, + getSupportedChains, makeValidateMsg, processError, } from '@dao-dao/utils' @@ -94,14 +101,23 @@ export interface CreateDaoFormProps { } export const CreateDaoForm = (props: CreateDaoFormProps) => { + const setWalletChainId = useSetRecoilState(walletChainIdAtom) const chainId = useRecoilValue( // If parent DAO exists, we're making a SubDAO, so use the parent DAO's // chain ID. props.parentDao ? constSelector(props.parentDao.chainId) : walletChainIdAtom ) + const config = getSupportedChainConfig(chainId) + // Switch to a valid chain if not a valid supported chain. + useEffect(() => { + if (!config) { + setWalletChainId(getSupportedChains()[0].chainId) + } + }, [config, setWalletChainId]) + if (!config) { - throw new Error('Unsupported chain.') + return } return ( diff --git a/packages/stateful/components/dao/DaoPageWrapper.tsx b/packages/stateful/components/dao/DaoPageWrapper.tsx index a8db353c3..fcdfb05cb 100644 --- a/packages/stateful/components/dao/DaoPageWrapper.tsx +++ b/packages/stateful/components/dao/DaoPageWrapper.tsx @@ -50,7 +50,8 @@ export const DaoPageWrapper = ({ const { setAccentColor, theme } = useThemeContext() const [walletChainId, setWalletChainId] = useRecoilState(walletChainIdAtom) - // Update walletChainId to whatever the current DAO is. + // Update walletChainId to whatever the current DAO is so the right address + // appears in the sidebar. useEffect(() => { if (serializedInfo && serializedInfo.chainId !== walletChainId) { setWalletChainId(serializedInfo.chainId) diff --git a/packages/stateful/components/dao/DaoTreasuryHistory.tsx b/packages/stateful/components/dao/DaoTreasuryHistory.tsx index dd4e427c7..389badcb9 100644 --- a/packages/stateful/components/dao/DaoTreasuryHistory.tsx +++ b/packages/stateful/components/dao/DaoTreasuryHistory.tsx @@ -17,8 +17,8 @@ import { LineGraph, Loader, useChainContext, + useConfiguredChainContext, useDaoInfoContext, - useSupportedChainContext, } from '@dao-dao/stateless' import { convertMicroDenomToDenomWithDecimals, @@ -260,7 +260,7 @@ const TransactionRenderer = ({ outgoing, }, }: TransactionRendererProps) => { - const { config } = useSupportedChainContext() + const { config } = useConfiguredChainContext() return (
diff --git a/packages/stateful/components/gov/GovPageWrapper.tsx b/packages/stateful/components/gov/GovPageWrapper.tsx index fb2180ccb..dde3aa825 100644 --- a/packages/stateful/components/gov/GovPageWrapper.tsx +++ b/packages/stateful/components/gov/GovPageWrapper.tsx @@ -46,7 +46,7 @@ export const GovPageWrapper = ({ const { setAccentColor, theme } = useThemeContext() const [walletChainId, setWalletChainId] = useRecoilState(walletChainIdAtom) - // Update walletChainId to current. + // Update walletChainId so the sidebar shows the correct wallet address. useEffect(() => { if (serializedInfo && serializedInfo.chainId !== walletChainId) { setWalletChainId(serializedInfo.chainId) diff --git a/packages/stateful/components/gov/GovProposalLine.tsx b/packages/stateful/components/gov/GovProposalLine.tsx index 5af5e9341..8ec84201d 100644 --- a/packages/stateful/components/gov/GovProposalLine.tsx +++ b/packages/stateful/components/gov/GovProposalLine.tsx @@ -4,7 +4,7 @@ import { GovProposalWalletVote, ProposalLineLoader, ProposalLine as StatelessProposalLine, - useSupportedChainContext, + useConfiguredChainContext, } from '@dao-dao/stateless' import { GovProposalWithDecodedContent } from '@dao-dao/types' import { getGovProposalPath } from '@dao-dao/utils' @@ -29,7 +29,9 @@ const InnerGovProposalLine = ({ proposalId, proposal, }: GovProposalLineProps) => { - const { config } = useSupportedChainContext() + const { + config: { name }, + } = useConfiguredChainContext() const loadingGovProp = useLoadingGovProposal(proposalId) return ( @@ -45,7 +47,7 @@ const InnerGovProposalLine = ({ status={proposal.proposal.status} /> )} - href={getGovProposalPath(config.name, proposalId)} + href={getGovProposalPath(name, proposalId)} proposalNumber={Number(proposalId)} proposalPrefix="" timestampDisplay={ diff --git a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx index de2c13b37..9a9430c1b 100644 --- a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx +++ b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx @@ -26,8 +26,8 @@ import { Tooltip, useCachedLoading, useChain, + useConfiguredChainContext, useGovProposalVoteOptions, - useSupportedChainContext, } from '@dao-dao/stateless' import { GenericToken, @@ -111,7 +111,7 @@ const InnerProposalStatusAndInfo = ({ const { chain: { chain_id: chainId, pretty_name: chainPrettyName }, config: { name: chainConfigName }, - } = useSupportedChainContext() + } = useConfiguredChainContext() const { isWalletConnected, address: walletAddress = '', @@ -405,7 +405,10 @@ const InnerProposalStatusAndInfoLoader = ( props: GovProposalStatusAndInfoProps ) => { const { t } = useTranslation() - const { config, chain } = useSupportedChainContext() + const { + config: { name }, + chain, + } = useConfiguredChainContext() const LoaderP: ComponentType<{ className: string }> = ({ className }) => (

...

@@ -417,11 +420,7 @@ const InnerProposalStatusAndInfoLoader = ( ), label: t('title.dao'), Value: (props) => ( - + {chain.pretty_name} ), diff --git a/packages/stateful/components/gov/NewGovProposal.tsx b/packages/stateful/components/gov/NewGovProposal.tsx index afe0274a0..ae9b743c2 100644 --- a/packages/stateful/components/gov/NewGovProposal.tsx +++ b/packages/stateful/components/gov/NewGovProposal.tsx @@ -53,9 +53,10 @@ import { TextInput, Tooltip, useCachedLoading, - useSupportedChainContext, + useConfiguredChainContext, } from '@dao-dao/stateless' import { + ActionChainContextType, ActionContextType, GovProposalVersion, GovernanceProposalActionData, @@ -99,7 +100,7 @@ enum ProposeSubmitValue { export const NewGovProposal = () => { const { t } = useTranslation() const router = useRouter() - const chainContext = useSupportedChainContext() + const chainContext = useConfiguredChainContext() const { isWalletConnected, getSigningStargateClient, chain, chainWallet } = useWallet() @@ -154,7 +155,11 @@ export const NewGovProposal = () => { const governanceProposalAction = makeGovernanceProposalAction({ t, chain: chainContext.chain, - chainContext, + chainContext: { + type: ActionChainContextType.Base, + ...chainContext, + ...chainContext.config, + }, address: walletAddress, context: { type: ActionContextType.Wallet, diff --git a/packages/stateful/components/pages/Me.tsx b/packages/stateful/components/pages/Me.tsx index d893759eb..a275bec06 100644 --- a/packages/stateful/components/pages/Me.tsx +++ b/packages/stateful/components/pages/Me.tsx @@ -16,7 +16,7 @@ import { SITE_URL, transformBech32Address } from '@dao-dao/utils' import { WalletActionsProvider } from '../../actions/react/provider' import { useWallet } from '../../hooks/useWallet' import { useWalletInfo } from '../../hooks/useWalletInfo' -import { ChainSwitcher } from '../ChainSwitcher' +import { AllConfiguredChainSwitcher } from '../ChainSwitcher' import { ConnectWallet } from '../ConnectWallet' import { ProfileDisconnectedCard, ProfileHomeCard } from '../profile' import { SuspenseLoader } from '../SuspenseLoader' @@ -63,7 +63,7 @@ export const Me: NextPage = () => { {/* Suspend to prevent hydration error since we load state on first render from localStorage. */} }> { const tokensWithoutLazyInfo = useCachedLoading( walletAddress ? waitForAllSettled( - getSupportedChains().map(({ chain }) => + getConfiguredChains().map(({ chain }) => walletTokenCardInfosSelector({ chainId: chain.chain_id, walletAddress: transformBech32Address( diff --git a/packages/stateful/components/wallet/WalletUi.tsx b/packages/stateful/components/wallet/WalletUi.tsx index 5d137efb5..93593cd8a 100644 --- a/packages/stateful/components/wallet/WalletUi.tsx +++ b/packages/stateful/components/wallet/WalletUi.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next' import { Modal, WarningCard } from '@dao-dao/stateless' import { - getSupportedChains, + getConfiguredChains, maybeGetAssetListForChainId, processError, } from '@dao-dao/utils' @@ -108,7 +108,7 @@ export const WalletUi = (props: WalletModalProps) => { // Connect to wallet. try { // Ensure supported chains are added before connecting. - const supportedChains = getSupportedChains().map(({ chain }) => + const configuredChains = getConfiguredChains().map(({ chain }) => convertChain( chain, [maybeGetAssetListForChainId(chain.chain_id)].filter( @@ -118,7 +118,7 @@ export const WalletUi = (props: WalletModalProps) => { ) await Promise.all( - supportedChains.map((chainRecord) => + configuredChains.map((chainRecord) => walletRepo .getWallet(wallet.walletName) ?.mainWallet.client.addChain?.(chainRecord) diff --git a/packages/stateful/hooks/useLoadingGovProposal.tsx b/packages/stateful/hooks/useLoadingGovProposal.tsx index 53c0355ab..825dae887 100644 --- a/packages/stateful/hooks/useLoadingGovProposal.tsx +++ b/packages/stateful/hooks/useLoadingGovProposal.tsx @@ -10,7 +10,7 @@ import { } from '@dao-dao/state' import { useCachedLoading, - useSupportedChainContext, + useConfiguredChainContext, useTranslatedTimeDeltaFormatter, } from '@dao-dao/stateless' import { @@ -36,7 +36,7 @@ export const useLoadingGovProposal = ( proposalId: string | number ): LoadingData => { const { t } = useTranslation() - const { chain } = useSupportedChainContext() + const { chain } = useConfiguredChainContext() const { address: voter } = useWallet() const loadingProposal = useCachedLoading( diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx index a1d192403..440102daa 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx @@ -24,9 +24,9 @@ import { ProposalStatusAndInfoProps, ProposalStatusAndInfo as StatelessProposalStatusAndInfo, TooltipTruncatedText, + useConfiguredChainContext, useDaoInfoContext, useDaoNavHelpers, - useSupportedChainContext, } from '@dao-dao/stateless' import { BaseProposalStatusAndInfoProps, @@ -108,7 +108,7 @@ const InnerProposalStatusAndInfo = ({ const { chain: { chain_id: chainId }, config: { explorerUrlTemplates }, - } = useSupportedChainContext() + } = useConfiguredChainContext() const { name: daoName, coreAddress } = useDaoInfoContext() const { getDaoPath } = useDaoNavHelpers() const { proposalModule, proposalNumber } = useProposalModuleAdapterOptions() diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx index e8b6bf2cf..ddf12aade 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx @@ -23,9 +23,9 @@ import { ProposalStatusAndInfoProps, ProposalStatusAndInfo as StatelessProposalStatusAndInfo, Tooltip, + useConfiguredChainContext, useDaoInfoContext, useDaoNavHelpers, - useSupportedChainContext, } from '@dao-dao/stateless' import { BaseProposalStatusAndInfoProps, @@ -122,7 +122,7 @@ const InnerProposalStatusAndInfo = ({ const { chain: { chain_id: chainId }, config: { explorerUrlTemplates }, - } = useSupportedChainContext() + } = useConfiguredChainContext() const { name: daoName, coreAddress } = useDaoInfoContext() const { getDaoPath } = useDaoNavHelpers() const { proposalModule, proposalNumber } = useProposalModuleAdapterOptions() diff --git a/packages/stateful/recoil/selectors/wallet.ts b/packages/stateful/recoil/selectors/wallet.ts index 34287d8e8..a36dfaaf2 100644 --- a/packages/stateful/recoil/selectors/wallet.ts +++ b/packages/stateful/recoil/selectors/wallet.ts @@ -30,9 +30,9 @@ import { KVPK_API_BASE, ME_SAVED_TX_PREFIX, convertMicroDenomToDenomWithDecimals, + getConfiguredChains, getFallbackImage, getNativeTokenForChainId, - getSupportedChains, transformBech32Address, } from '@dao-dao/utils' @@ -288,7 +288,7 @@ export const allWalletNftsSelector = selectorFamily< get: ({ walletAddress }) => ({ get }) => { - const chains = getSupportedChains() + const chains = getConfiguredChains().filter((c) => !c.noCosmWasm) const nativeNfts = get( waitForAll( @@ -383,7 +383,7 @@ export const allWalletDaosSelector = selectorFamily< get: ({ walletAddress }) => ({ get }) => { - const chains = getSupportedChains() + const chains = getConfiguredChains() const allLazyDaoCards = get( waitForAll( diff --git a/packages/stateful/server/makeGetGovStaticProps.ts b/packages/stateful/server/makeGetGovStaticProps.ts index 161ab68d0..46ac0b294 100644 --- a/packages/stateful/server/makeGetGovStaticProps.ts +++ b/packages/stateful/server/makeGetGovStaticProps.ts @@ -18,10 +18,10 @@ import { SITE_URL, cosmosSdkVersionIs47OrHigher, decodeGovProposal, + getConfiguredChains, getGovProposalPath, getImageUrlForChainId, getRpcForChainId, - getSupportedChains, processError, } from '@dao-dao/utils' import { cosmos } from '@dao-dao/utils/protobuf' @@ -68,11 +68,11 @@ export const makeGetGovStaticProps: GetGovStaticPropsMaker = ['translation'] ) - const supportedChain = context.params?.chain - ? getSupportedChains().find(({ name }) => name === context.params?.chain) + const chainConfig = context.params?.chain + ? getConfiguredChains().find(({ name }) => name === context.params?.chain) : undefined - if (!supportedChain) { + if (!chainConfig) { // Excluding `info` will render not found. return { props: { @@ -86,7 +86,7 @@ export const makeGetGovStaticProps: GetGovStaticPropsMaker = } } - const { chain, accentColor } = supportedChain + const { chain, accentColor } = chainConfig // Must be called after server side translations has been awaited, because // props may use the `t` function, and it won't be available until after. @@ -117,7 +117,7 @@ export const makeGetGovStaticProps: GetGovStaticPropsMaker = accentColor, serializedInfo: { chainId: chain.chain_id, - coreAddress: supportedChain.name, + coreAddress: chainConfig.name, coreVersion: ContractVersion.Gov, supportedFeatures: Object.values(Feature).reduce( (acc, feature) => ({ diff --git a/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx b/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx index 3b10fd3b1..f41d63244 100644 --- a/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx +++ b/packages/stateful/widgets/widgets/WyndDeposit/WyndDepositRenderer.tsx @@ -28,8 +28,8 @@ import { TokenAmountDisplay, useCachedLoadable, useCachedLoading, + useConfiguredChainContext, useDaoInfoContext, - useSupportedChainContext, } from '@dao-dao/stateless' import { GenericToken, @@ -71,7 +71,7 @@ export const WyndDepositRenderer = ({ const { chain: { chain_id: chainId }, config: { explorerUrlTemplates }, - } = useSupportedChainContext() + } = useConfiguredChainContext() const { coreAddress } = useDaoInfoContext() // Default to the DAO's treasury if no output specified. diff --git a/packages/stateless/components/ChainProvider.tsx b/packages/stateless/components/ChainProvider.tsx index 92d34e52b..95b4e63ba 100644 --- a/packages/stateless/components/ChainProvider.tsx +++ b/packages/stateless/components/ChainProvider.tsx @@ -2,6 +2,7 @@ import { ReactNode } from 'react' import { getChainForChainId, + getConfiguredChainConfig, getSupportedChainConfig, maybeGetNativeTokenForChainId, } from '@dao-dao/utils' @@ -19,6 +20,7 @@ export const ChainProvider = ({ chainId, children }: ChainProviderProps) => ( chainId, chain: getChainForChainId(chainId), nativeToken: maybeGetNativeTokenForChainId(chainId), + base: getConfiguredChainConfig(chainId), config: getSupportedChainConfig(chainId), }} > diff --git a/packages/stateless/components/inputs/ChainSwitcher.tsx b/packages/stateless/components/inputs/ChainSwitcher.tsx index 9a7999680..d6cbe24f2 100644 --- a/packages/stateless/components/inputs/ChainSwitcher.tsx +++ b/packages/stateless/components/inputs/ChainSwitcher.tsx @@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next' import { ButtonPopupProps, ButtonPopupSectionButton } from '@dao-dao/types' import { getChainForChainId, + getConfiguredChains, getDisplayNameForChainId, getSupportedChains, } from '@dao-dao/utils' @@ -19,6 +20,9 @@ export type ChainSwitcherProps = Omit< ButtonPopupProps, 'ButtonLink' | 'position' | 'sections' | 'trigger' > & { + // Configured refers to all configured chains, even those without DAO DAO + // deployments. This includes all chains that show up in the governance UI. + type?: 'configured' | 'supported' position?: ButtonPopupProps['position'] loading?: boolean excludeChainIds?: string[] @@ -53,6 +57,7 @@ type ChainSwitcherButton = Omit & { } export const ChainSwitcher = ({ + type = 'supported', onSelect, selected, loading, @@ -99,7 +104,10 @@ export const ChainSwitcher = ({ }, ] as ChainSwitcherButton[]) : []), - ...getSupportedChains() + ...(type === 'supported' + ? getSupportedChains() + : getConfiguredChains() + ) .filter( ({ chain: { chain_id: chainId } }) => !excludeChainIdsRef.current?.includes(chainId) @@ -112,10 +120,17 @@ export const ChainSwitcher = ({ Icon: makeChainIcon(chain.chain_id), }) ) - .sort((a, b) => + .sort((a, b) => { + // Sort selected to the top. + if (a.pressed && !b.pressed) { + return -1 + } else if (!a.pressed && b.pressed) { + return 1 + } + // Sort alphabetically by label. - a.label.localeCompare(b.label) - ), + return a.label.localeCompare(b.label) + }), ], }, ] @@ -125,7 +140,7 @@ export const ChainSwitcher = ({ chainSwitcherTriggerContent, chainSwitcherSections, } - }, [chain, noneIcon, noneLabel, selected, showNone]) + }, [chain, noneIcon, noneLabel, selected, showNone, type]) return ( ( diff --git a/packages/stateless/hooks/useChainContext.ts b/packages/stateless/hooks/useChainContext.ts index 9e28068b4..f12d13ca0 100644 --- a/packages/stateless/hooks/useChainContext.ts +++ b/packages/stateless/hooks/useChainContext.ts @@ -1,6 +1,10 @@ -import { createContext, useContext } from 'react' +import { createContext, useContext, useMemo } from 'react' -import { IChainContext, SupportedChainContext } from '@dao-dao/types' +import { + ConfiguredChainContext, + IChainContext, + SupportedChainContext, +} from '@dao-dao/types' export const ChainContext = createContext(null) @@ -18,6 +22,23 @@ export const useChainContext = (): IChainContext => { return context } +export const useConfiguredChainContext = (): ConfiguredChainContext => { + const context = useChainContext() + + // Make sure this is a configured chain. + if (!context.base) { + throw new Error('Unconfigured chain context.') + } + + return useMemo( + (): ConfiguredChainContext => ({ + ...context, + config: context.base!, + }), + [context] + ) +} + export const useSupportedChainContext = (): SupportedChainContext => { const context = useChainContext() diff --git a/packages/types/actions.ts b/packages/types/actions.ts index db96f6dcb..423540b45 100644 --- a/packages/types/actions.ts +++ b/packages/types/actions.ts @@ -4,7 +4,7 @@ import { ComponentType } from 'react' import { FieldErrors } from 'react-hook-form' import { TFunction } from 'react-i18next' -import { SupportedChainContext } from './chain' +import { ConfiguredChainContext, SupportedChainContext } from './chain' import { CosmosMsgFor_Empty } from './contracts/common' import { DaoInfo } from './dao' import { AllGovParams } from './gov' @@ -241,10 +241,23 @@ export type ActionContext = params: AllGovParams } +export enum ActionChainContextType { + Base = 'base', + Supported = 'supported', +} + +export type ActionChainContext = + | ({ + type: ActionChainContextType.Base + } & ConfiguredChainContext) + | ({ + type: ActionChainContextType.Supported + } & SupportedChainContext) + export type ActionOptions = ExtraOptions & { t: TFunction chain: Chain - chainContext: SupportedChainContext + chainContext: ActionChainContext // The address of the sender/actor. // DAO core address if context.type === Dao // Wallet address if context.type === Wallet diff --git a/packages/types/chain.ts b/packages/types/chain.ts index 41912811e..ac5f7aa4a 100644 --- a/packages/types/chain.ts +++ b/packages/types/chain.ts @@ -10,12 +10,20 @@ export type IChainContext = { chain: Chain // Chain may not have a native token. nativeToken?: GenericToken - // If defined, this is a supported chain. + // If defined, this is a configured chain, which means it is supported (DAO + // DAO is deployed on it) or it has a governance interface. + base?: BaseChainConfig + // If defined, this is a supported chain, which means DAO DAO is deployed. config?: SupportedChainConfig } +// Require base chain config. +export type ConfiguredChainContext = Omit & { + config: BaseChainConfig +} + // Require supported chain config. -export type SupportedChainContext = Omit & { +export type SupportedChainContext = Omit & { config: SupportedChainConfig } @@ -48,6 +56,7 @@ export interface NativeDelegationInfo { } export enum ChainId { + CosmosHubMainnet = 'cosmoshub-4', JunoMainnet = 'juno-1', JunoTestnet = 'uni-6', OsmosisMainnet = 'osmosis-1', @@ -57,17 +66,36 @@ export enum ChainId { NeutronMainnet = 'neutron-1', } -export type SupportedChainConfig = { +export type BaseChainConfig = { + chainId: string // Unique name among chain configs with the same `mainnet` flag. This is used // to identify the chain in the native governance UI. name: string mainnet: boolean accentColor: string + // Set to true if the chain does not support CosmWasm. If undefined, assumed + // to be false. + noCosmWasm?: boolean + gov: { + // Supports new v1 gov proposals introduced in cosmos-sdk v47. Some chains + // that fork the SDK, like Osmosis, don't support v1 gov proposals even + // though they use cosmos-sdk v47 or higher, so we need a hardcoded flag. + supportsV1GovProposals: boolean + } + explorerUrlTemplates: { + tx: string + gov: string + govProp: string + wallet: string + } +} + +export type ConfiguredChain = BaseChainConfig & { + chain: Chain +} + +export type SupportedChainConfig = BaseChainConfig & { factoryContractAddress: string - // Supports new v1 gov proposals introduced in cosmos-sdk v47. Some chains - // that fork the SDK, like Osmosis, don't support v1 gov proposals even though - // they use cosmos-sdk v47 or higher, so we need a hardcoded flag. - supportsV1GovProposals: boolean // If defined, it means Kado supports fiat deposit on this chain. kado?: { network: string @@ -76,12 +104,6 @@ export type SupportedChainConfig = { search: string featured: string } - explorerUrlTemplates: { - tx: string - gov: string - govProp: string - wallet: string - } codeIds: CodeIdConfig // Store code IDs for past versions of contracts, in case DAOs need a // particular version of a contract. diff --git a/packages/utils/chain.ts b/packages/utils/chain.ts index 312aeda8f..1aebd03fc 100644 --- a/packages/utils/chain.ts +++ b/packages/utils/chain.ts @@ -8,7 +8,8 @@ import { assets, chains, ibc } from 'chain-registry' import RIPEMD160 from 'ripemd160' import { - ChainId, + BaseChainConfig, + ConfiguredChain, GenericToken, SupportedChain, SupportedChainConfig, @@ -18,7 +19,12 @@ import { import { cosmos } from '@dao-dao/utils/protobuf' import { getChainAssets } from './assets' -import { CHAIN_ENDPOINTS, MAINNET, SUPPORTED_CHAINS } from './constants' +import { + CHAIN_ENDPOINTS, + CONFIGURED_CHAINS, + MAINNET, + SUPPORTED_CHAINS, +} from './constants' import { getFallbackImage } from './getFallbackImage' import { aminoTypes, typesRegistry } from './messages/protobuf' import { @@ -372,24 +378,39 @@ export const getIbcTransferInfoFromChainSource = ( } } +export const getConfiguredChainConfig = ( + chainId: string +): BaseChainConfig | undefined => + CONFIGURED_CHAINS.find((config) => config.chainId === chainId) + +export const getConfiguredChains = ({ + mainnet = MAINNET, +}: { + mainnet?: boolean +} = {}): ConfiguredChain[] => + CONFIGURED_CHAINS.filter( + (config) => mainnet === undefined || config.mainnet === mainnet + ).map((config) => ({ + chain: getChainForChainId(config.chainId), + ...config, + })) + export const getSupportedChainConfig = ( chainId: string ): SupportedChainConfig | undefined => - Object.values(ChainId).includes(chainId as any) - ? SUPPORTED_CHAINS[chainId as ChainId] - : undefined + SUPPORTED_CHAINS.find((config) => config.chainId === chainId) export const getSupportedChains = ({ mainnet = MAINNET, }: { mainnet?: boolean } = {}): SupportedChain[] => - Object.entries(SUPPORTED_CHAINS) - .filter(([, config]) => mainnet === undefined || config.mainnet === mainnet) - .map(([chainId, config]) => ({ - chain: getChainForChainId(chainId), - ...config, - })) + SUPPORTED_CHAINS.filter( + (config) => mainnet === undefined || config.mainnet === mainnet + ).map((config) => ({ + chain: getChainForChainId(config.chainId), + ...config, + })) // Validates whether the address is for the current chain. If so, return // undefined. If not, return the correct subdomain. diff --git a/packages/utils/constants/chains.ts b/packages/utils/constants/chains.ts index bffbb28ff..00bdb8bcd 100644 --- a/packages/utils/constants/chains.ts +++ b/packages/utils/constants/chains.ts @@ -1,4 +1,5 @@ import { + BaseChainConfig, ChainId, ContractVersion, PolytoneConfig, @@ -6,481 +7,501 @@ import { } from '@dao-dao/types' // Chains which DAO DAO DAOs exist on. -export const SUPPORTED_CHAINS: Partial> = +export const SUPPORTED_CHAINS: SupportedChainConfig[] = [ { - [ChainId.JunoMainnet]: { - name: 'juno', - mainnet: true, - accentColor: '#f74a49', - factoryContractAddress: - 'juno1eeqgsjyqxcscpxwa6ut36py8vfpu6hxrwy62n2vgu8ud72wa9pyqv38q7y', + chainId: ChainId.JunoMainnet, + name: 'juno', + mainnet: true, + accentColor: '#f74a49', + factoryContractAddress: + 'juno1eeqgsjyqxcscpxwa6ut36py8vfpu6hxrwy62n2vgu8ud72wa9pyqv38q7y', + kado: { + network: 'JUNO', + }, + indexes: { + search: 'daos', + featured: 'featured_daos', + }, + explorerUrlTemplates: { + tx: 'https://ping.pub/juno/tx/REPLACE', + gov: 'https://ping.pub/juno/gov', + govProp: 'https://ping.pub/juno/gov/REPLACE', + wallet: 'https://ping.pub/juno/account/REPLACE', + }, + gov: { supportsV1GovProposals: true, - kado: { - network: 'JUNO', - }, - indexes: { - search: 'daos', - featured: 'featured_daos', - }, - explorerUrlTemplates: { - tx: 'https://ping.pub/juno/tx/REPLACE', - gov: 'https://ping.pub/juno/gov', - govProp: 'https://ping.pub/juno/gov/REPLACE', - wallet: 'https://ping.pub/juno/account/REPLACE', - }, - codeIds: { - // https://github.com/CosmWasm/cw-plus - Cw4Group: 1992, // v0.16 - // https://github.com/CosmWasm/cw-nfts - Cw721Base: 1994, // v0.16 + }, + codeIds: { + // https://github.com/CosmWasm/cw-plus + Cw4Group: 1992, // v0.16 + // https://github.com/CosmWasm/cw-nfts + Cw721Base: 1994, // v0.16 - // ContractVersion.V230 - CwPayrollFactory: 3822, - CwTokenSwap: 3823, - CwTokenfactoryIssuer: 3824, - CwVesting: 3825, - DaoCore: 3826, - DaoMigrator: 3827, - DaoPreProposeMultiple: 3828, - DaoPreProposeSingle: 3829, - DaoProposalMultiple: 3830, - DaoProposalSingle: 3831, - DaoVotingCw4: 3832, - DaoVotingCw721Staked: 3833, - DaoVotingTokenStaked: 3834, + // ContractVersion.V230 + CwPayrollFactory: 3822, + CwTokenSwap: 3823, + CwTokenfactoryIssuer: 3824, + CwVesting: 3825, + DaoCore: 3826, + DaoMigrator: 3827, + DaoPreProposeMultiple: 3828, + DaoPreProposeSingle: 3829, + DaoProposalMultiple: 3830, + DaoProposalSingle: 3831, + DaoVotingCw4: 3832, + DaoVotingCw721Staked: 3833, + DaoVotingTokenStaked: 3834, - // v2.1.0 and below, for migrating v1 to v2 DAOs - // ContractVersion.V210 - Cw20Stake: 2444, - DaoVotingCw20Staked: 2463, - }, - historicalCodeIds: { - [ContractVersion.V210]: { - DaoPreProposeMultiple: 2458, - DaoProposalMultiple: 2461, - }, + // v2.1.0 and below, for migrating v1 to v2 DAOs + // ContractVersion.V210 + Cw20Stake: 2444, + DaoVotingCw20Staked: 2463, + }, + historicalCodeIds: { + [ContractVersion.V210]: { + DaoPreProposeMultiple: 2458, + DaoProposalMultiple: 2461, }, - polytone: { - [ChainId.OsmosisMainnet]: { - // juno - note: 'juno1ads7gcpje0y5jxhtn3ntsqs8kg3ahch9u953jk6v0njq4l39m3us5sxw68', - // juno - listener: - 'juno1f2676a53wxnnp05ezch69hwp5lpxug5qm07lyeywlf57y9ghw46qylrshd', - // osmosis - voice: - 'osmo1af93h8xcszszes2a0kjms5zpm5ns3fys4aez2f40fgz428hc8aws28klzs', - // juno - localConnection: 'connection-0', - // osmosis - remoteConnection: 'connection-1142', - // juno - localChannel: 'channel-288', - // osmosis - remoteChannel: 'channel-1664', - // juno - // localClient: '07-tendermint-0', - // osmosis - // remoteClient: '07-tendermint-1457', - }, - [ChainId.StargazeMainnet]: { - // juno - note: 'juno1vupyxq9q2mmg5jjcd4cl0ujav8a3xn0a9ahyhtmj0zjaje2gfejsyvyf3z', - // juno - listener: - 'juno1w9q8dgfl0n59gpuagn2r8j89w6y5ad4z8yvct096zawksaevx2nqzw3x9q', - // stargaze - voice: - 'stars1g9u4zmjj3xmu2me3vq07fqedqp7t0d9xjp3tqff9r2awwc2k8wvq7d39he', - // juno - localConnection: 'connection-30', - // stargaze - remoteConnection: 'connection-11', - // juno - localChannel: 'channel-305', - // stargaze - remoteChannel: 'channel-201', - // juno - // localClient: '07-tendermint-44', - // stargaze - // remoteClient: '07-tendermint-13', - }, + }, + polytone: { + [ChainId.OsmosisMainnet]: { + // juno + note: 'juno1ads7gcpje0y5jxhtn3ntsqs8kg3ahch9u953jk6v0njq4l39m3us5sxw68', + // juno + listener: + 'juno1f2676a53wxnnp05ezch69hwp5lpxug5qm07lyeywlf57y9ghw46qylrshd', + // osmosis + voice: + 'osmo1af93h8xcszszes2a0kjms5zpm5ns3fys4aez2f40fgz428hc8aws28klzs', + // juno + localConnection: 'connection-0', + // osmosis + remoteConnection: 'connection-1142', + // juno + localChannel: 'channel-288', + // osmosis + remoteChannel: 'channel-1664', + // juno + // localClient: '07-tendermint-0', + // osmosis + // remoteClient: '07-tendermint-1457', + }, + [ChainId.StargazeMainnet]: { + // juno + note: 'juno1vupyxq9q2mmg5jjcd4cl0ujav8a3xn0a9ahyhtmj0zjaje2gfejsyvyf3z', + // juno + listener: + 'juno1w9q8dgfl0n59gpuagn2r8j89w6y5ad4z8yvct096zawksaevx2nqzw3x9q', + // stargaze + voice: + 'stars1g9u4zmjj3xmu2me3vq07fqedqp7t0d9xjp3tqff9r2awwc2k8wvq7d39he', + // juno + localConnection: 'connection-30', + // stargaze + remoteConnection: 'connection-11', + // juno + localChannel: 'channel-305', + // stargaze + remoteChannel: 'channel-201', + // juno + // localClient: '07-tendermint-44', + // stargaze + // remoteClient: '07-tendermint-13', }, }, - [ChainId.OsmosisMainnet]: { - name: 'osmosis', - mainnet: true, - accentColor: '#5604e8', - factoryContractAddress: - 'osmo102pg8quxtvhye3k4rcqwh7j5zwf5ekhcvlquafjjxjnarhu38qzstkdm6p', + }, + { + chainId: ChainId.OsmosisMainnet, + name: 'osmosis', + mainnet: true, + accentColor: '#5604e8', + factoryContractAddress: + 'osmo102pg8quxtvhye3k4rcqwh7j5zwf5ekhcvlquafjjxjnarhu38qzstkdm6p', + kado: { + network: 'OSMOSIS', + }, + indexes: { + search: 'osmosis_daos', + // Use same as mainnet. + featured: 'osmosis_featured_daos', + }, + explorerUrlTemplates: { + tx: 'https://ping.pub/osmosis/tx/REPLACE', + gov: 'https://ping.pub/osmosis/gov', + govProp: 'https://ping.pub/osmosis/gov/REPLACE', + wallet: 'https://ping.pub/osmosis/account/REPLACE', + }, + gov: { supportsV1GovProposals: false, - kado: { - network: 'OSMOSIS', - }, - indexes: { - search: 'osmosis_daos', - // Use same as mainnet. - featured: 'osmosis_featured_daos', - }, - explorerUrlTemplates: { - tx: 'https://ping.pub/osmosis/tx/REPLACE', - gov: 'https://ping.pub/osmosis/gov', - govProp: 'https://ping.pub/osmosis/gov/REPLACE', - wallet: 'https://ping.pub/osmosis/account/REPLACE', - }, - codeIds: { - // https://github.com/CosmWasm/cw-plus - Cw4Group: 123, // v0.16 - // https://github.com/CosmWasm/cw-nfts - Cw721Base: 124, // v0.16 + }, + codeIds: { + // https://github.com/CosmWasm/cw-plus + Cw4Group: 123, // v0.16 + // https://github.com/CosmWasm/cw-nfts + Cw721Base: 124, // v0.16 - // ContractVersion.V230 - CwPayrollFactory: 241, - CwTokenSwap: 242, - CwTokenfactoryIssuer: 243, - CwVesting: 244, - DaoCore: 245, - DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 246 - DaoPreProposeMultiple: 247, - DaoPreProposeSingle: 248, - DaoProposalMultiple: 249, - DaoProposalSingle: 250, - DaoVotingCw4: 251, - DaoVotingCw721Staked: 253, - DaoVotingTokenStaked: 252, - }, - historicalCodeIds: { - [ContractVersion.V210]: { - DaoPreProposeMultiple: 118, - DaoProposalMultiple: 120, - }, + // ContractVersion.V230 + CwPayrollFactory: 241, + CwTokenSwap: 242, + CwTokenfactoryIssuer: 243, + CwVesting: 244, + DaoCore: 245, + DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 246 + DaoPreProposeMultiple: 247, + DaoPreProposeSingle: 248, + DaoProposalMultiple: 249, + DaoProposalSingle: 250, + DaoVotingCw4: 251, + DaoVotingCw721Staked: 253, + DaoVotingTokenStaked: 252, + }, + historicalCodeIds: { + [ContractVersion.V210]: { + DaoPreProposeMultiple: 118, + DaoProposalMultiple: 120, }, - polytone: { - [ChainId.JunoMainnet]: { - // osmosis - note: 'osmo1zu9sa2yu9ffdk6pxsgjzgp56wqgyzdh8e0ndn7crr3d0xhvtj8uqdv3dqa', - // osmosis - listener: - 'osmo1jhwx9nunu4m3ajhvlm5vl2pltrhkltyawanp9c0qhxmxp940dessqh46k6', - // juno - voice: - 'juno1mkq8ggvmr7kzu85c9muud30nmdcv98050uxyqrqmmftlmag044gs3e0d0u', - // osmosis - localConnection: 'connection-1142', - // juno - remoteConnection: 'connection-0', - // osmosis - localChannel: 'channel-1656', - // juno - remoteChannel: 'channel-287', - // osmosis - // localClient: '07-tendermint-1457', - // juno - // remoteClient: '07-tendermint-0', - }, - [ChainId.StargazeMainnet]: { - // osmosis - note: 'osmo1u44zc3vq37npnctdwd35n7ms3wtagfhdvdnpscq86pk6h9rmzpcsdqe992', - // osmosis - listener: - 'osmo1d00cmsk7uym7mtrsrcnhhdza8mpu346klhrpufkzzxkvy9wlegeqw2my6l', - // stargaze - voice: - 'stars1fr7ccflazj6mfmpt8z2st424kxkpc6uw7t65sx80q5rp0s8kug6sj34avq', - // osmosis - localConnection: 'connection-1223', - // stargaze - remoteConnection: 'connection-0', - // osmosis - localChannel: 'channel-2659', - // stargaze - remoteChannel: 'channel-202', - // osmosis - // localClient: '07-tendermint-1562', - // stargaze - // remoteClient: '07-tendermint-0', - }, + }, + polytone: { + [ChainId.JunoMainnet]: { + // osmosis + note: 'osmo1zu9sa2yu9ffdk6pxsgjzgp56wqgyzdh8e0ndn7crr3d0xhvtj8uqdv3dqa', + // osmosis + listener: + 'osmo1jhwx9nunu4m3ajhvlm5vl2pltrhkltyawanp9c0qhxmxp940dessqh46k6', + // juno + voice: + 'juno1mkq8ggvmr7kzu85c9muud30nmdcv98050uxyqrqmmftlmag044gs3e0d0u', + // osmosis + localConnection: 'connection-1142', + // juno + remoteConnection: 'connection-0', + // osmosis + localChannel: 'channel-1656', + // juno + remoteChannel: 'channel-287', + // osmosis + // localClient: '07-tendermint-1457', + // juno + // remoteClient: '07-tendermint-0', + }, + [ChainId.StargazeMainnet]: { + // osmosis + note: 'osmo1u44zc3vq37npnctdwd35n7ms3wtagfhdvdnpscq86pk6h9rmzpcsdqe992', + // osmosis + listener: + 'osmo1d00cmsk7uym7mtrsrcnhhdza8mpu346klhrpufkzzxkvy9wlegeqw2my6l', + // stargaze + voice: + 'stars1fr7ccflazj6mfmpt8z2st424kxkpc6uw7t65sx80q5rp0s8kug6sj34avq', + // osmosis + localConnection: 'connection-1223', + // stargaze + remoteConnection: 'connection-0', + // osmosis + localChannel: 'channel-2659', + // stargaze + remoteChannel: 'channel-202', + // osmosis + // localClient: '07-tendermint-1562', + // stargaze + // remoteClient: '07-tendermint-0', }, }, - [ChainId.NeutronMainnet]: { - name: 'neutron', - mainnet: true, - accentColor: '#000000', - factoryContractAddress: - 'neutron1xms03jykg6e2g402dxj3cw4q6ygm0r5rctdt5d7j99xehwtevm3sxl52n5', + }, + { + chainId: ChainId.NeutronMainnet, + name: 'neutron', + mainnet: true, + accentColor: '#000000', + factoryContractAddress: + 'neutron1xms03jykg6e2g402dxj3cw4q6ygm0r5rctdt5d7j99xehwtevm3sxl52n5', + indexes: { + search: 'neutron_daos', + featured: 'neutron_featured_daos', + }, + explorerUrlTemplates: { + tx: 'https://ping.pub/neutron/tx/REPLACE', + gov: 'https://ping.pub/neutron/gov', + govProp: 'https://ping.pub/neutron/gov/REPLACE', + wallet: 'https://ping.pub/neutron/account/REPLACE', + }, + gov: { supportsV1GovProposals: false, - indexes: { - search: 'neutron_daos', - featured: 'neutron_featured_daos', - }, - explorerUrlTemplates: { - tx: 'https://ping.pub/neutron/tx/REPLACE', - gov: 'https://ping.pub/neutron/gov', - govProp: 'https://ping.pub/neutron/gov/REPLACE', - wallet: 'https://ping.pub/neutron/account/REPLACE', - }, - codeIds: { - // https://github.com/CosmWasm/cw-plus - Cw4Group: 218, // v0.16 - Cw721Base: 232, + }, + codeIds: { + // https://github.com/CosmWasm/cw-plus + Cw4Group: 218, // v0.16 + Cw721Base: 232, - // TODO(neutron-2.3.0): upgrade to v2.3.0 once CW 1.1 is supported - // ContractVersion.V210 - CwPayrollFactory: 220, - CwTokenSwap: 221, - CwTokenfactoryIssuer: -1, - CwVesting: 222, - DaoCore: 223, - DaoMigrator: -1, + // TODO(neutron-2.3.0): upgrade to v2.3.0 once CW 1.1 is supported + // ContractVersion.V210 + CwPayrollFactory: 220, + CwTokenSwap: 221, + CwTokenfactoryIssuer: -1, + CwVesting: 222, + DaoCore: 223, + DaoMigrator: -1, + DaoPreProposeMultiple: 224, + DaoPreProposeSingle: 225, + DaoProposalMultiple: 226, + DaoProposalSingle: 227, + DaoVotingCw4: 228, + DaoVotingCw721Staked: -1, + DaoVotingTokenStaked: -1, + }, + historicalCodeIds: { + [ContractVersion.V210]: { DaoPreProposeMultiple: 224, - DaoPreProposeSingle: 225, DaoProposalMultiple: 226, - DaoProposalSingle: 227, - DaoVotingCw4: 228, - DaoVotingCw721Staked: -1, - DaoVotingTokenStaked: -1, - }, - historicalCodeIds: { - [ContractVersion.V210]: { - DaoPreProposeMultiple: 224, - DaoProposalMultiple: 226, - }, }, }, - [ChainId.StargazeMainnet]: { - name: 'stargaze', - mainnet: true, - accentColor: '#8ac3cc', - factoryContractAddress: - 'stars175zvu8psmyxlszsxaa5thz26gjm4y6l24cr9ctgs09g90755tpmqmskl4t', + }, + { + chainId: ChainId.StargazeMainnet, + name: 'stargaze', + mainnet: true, + accentColor: '#8ac3cc', + factoryContractAddress: + 'stars175zvu8psmyxlszsxaa5thz26gjm4y6l24cr9ctgs09g90755tpmqmskl4t', + indexes: { + search: 'stargaze_daos', + featured: 'stargaze_featured_daos', + }, + explorerUrlTemplates: { + tx: 'https://ping.pub/stargaze/tx/REPLACE', + gov: 'https://ping.pub/stargaze/gov', + govProp: 'https://ping.pub/stargaze/gov/REPLACE', + wallet: 'https://ping.pub/stargaze/account/REPLACE', + }, + gov: { supportsV1GovProposals: false, - indexes: { - search: 'stargaze_daos', - featured: 'stargaze_featured_daos', - }, - explorerUrlTemplates: { - tx: 'https://ping.pub/stargaze/tx/REPLACE', - gov: 'https://ping.pub/stargaze/gov', - govProp: 'https://ping.pub/stargaze/gov/REPLACE', - wallet: 'https://ping.pub/stargaze/account/REPLACE', - }, - codeIds: { - // https://github.com/CosmWasm/cw-plus - Cw4Group: 83, // v0.16 + }, + codeIds: { + // https://github.com/CosmWasm/cw-plus + Cw4Group: 83, // v0.16 - // ContractVersion.V230 - CwPayrollFactory: 124, - CwTokenSwap: 125, - CwVesting: 126, - DaoCore: 127, - DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 128 - DaoPreProposeMultiple: 129, - DaoPreProposeSingle: 130, - DaoProposalMultiple: 131, - DaoProposalSingle: 132, - DaoVotingCw4: 133, - DaoVotingCw721Staked: 120, - DaoVotingTokenStaked: 121, - CwTokenfactoryIssuer: 122, - }, - historicalCodeIds: { - [ContractVersion.V210]: { - DaoPreProposeMultiple: 89, - DaoProposalMultiple: 91, - }, + // ContractVersion.V230 + CwPayrollFactory: 124, + CwTokenSwap: 125, + CwVesting: 126, + DaoCore: 127, + DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 128 + DaoPreProposeMultiple: 129, + DaoPreProposeSingle: 130, + DaoProposalMultiple: 131, + DaoProposalSingle: 132, + DaoVotingCw4: 133, + DaoVotingCw721Staked: 120, + DaoVotingTokenStaked: 121, + CwTokenfactoryIssuer: 122, + }, + historicalCodeIds: { + [ContractVersion.V210]: { + DaoPreProposeMultiple: 89, + DaoProposalMultiple: 91, }, - polytone: { - [ChainId.OsmosisMainnet]: { - // stargaze - note: 'stars1p4f96xz9pz8264ccgapz2l6xu82l5cj0jvvng0ltlm3dw2sxqdrs43acfl', - // stargaze - listener: - 'stars1cu9nkty3wrg997qnmsdtpcy0m448zu5zj6kxmjfuze7jj2t6m3ns3f7ry5', - // osmosis - voice: - 'osmo13w3073l43gwxw77tv2np2katn3jrvet87unyfevg8nrj755m3x7q0aaw63', - // stargaze - localConnection: 'connection-0', - // osmosis - remoteConnection: 'connection-1223', - // stargaze - localChannel: 'channel-198', - // osmosis - remoteChannel: 'channel-2642', - // stargaze - // localClient: '07-tendermint-0', - // osmosis - // remoteClient: '07-tendermint-1562', - }, - [ChainId.JunoMainnet]: { - // stargaze - note: 'stars17vst9ew3vhddgj4je82vdn0evv3dc9gyf0yapjydt9fzqn8c4ecqyunk79', - // stargaze - listener: - 'stars18mw7avlq5t0anxsavca5ch7ju0w6mjwu0jz55exfnhp0wz7rchasxurdf8', - // juno - voice: - 'juno13yxra87ltv7gva3z35ktxt0nx3n5tp8ngtkj2p2zxj0qg6n906fs00wgvf', - // stargaze - localConnection: 'connection-11', - // juno - remoteConnection: 'connection-30', - // stargaze - localChannel: 'channel-199', - // juno - remoteChannel: 'channel-304', - // stargaze - // localClient: '07-tendermint-13', - // juno - // remoteClient: '07-tendermint-44', - }, + }, + polytone: { + [ChainId.OsmosisMainnet]: { + // stargaze + note: 'stars1p4f96xz9pz8264ccgapz2l6xu82l5cj0jvvng0ltlm3dw2sxqdrs43acfl', + // stargaze + listener: + 'stars1cu9nkty3wrg997qnmsdtpcy0m448zu5zj6kxmjfuze7jj2t6m3ns3f7ry5', + // osmosis + voice: + 'osmo13w3073l43gwxw77tv2np2katn3jrvet87unyfevg8nrj755m3x7q0aaw63', + // stargaze + localConnection: 'connection-0', + // osmosis + remoteConnection: 'connection-1223', + // stargaze + localChannel: 'channel-198', + // osmosis + remoteChannel: 'channel-2642', + // stargaze + // localClient: '07-tendermint-0', + // osmosis + // remoteClient: '07-tendermint-1562', + }, + [ChainId.JunoMainnet]: { + // stargaze + note: 'stars17vst9ew3vhddgj4je82vdn0evv3dc9gyf0yapjydt9fzqn8c4ecqyunk79', + // stargaze + listener: + 'stars18mw7avlq5t0anxsavca5ch7ju0w6mjwu0jz55exfnhp0wz7rchasxurdf8', + // juno + voice: + 'juno13yxra87ltv7gva3z35ktxt0nx3n5tp8ngtkj2p2zxj0qg6n906fs00wgvf', + // stargaze + localConnection: 'connection-11', + // juno + remoteConnection: 'connection-30', + // stargaze + localChannel: 'channel-199', + // juno + remoteChannel: 'channel-304', + // stargaze + // localClient: '07-tendermint-13', + // juno + // remoteClient: '07-tendermint-44', }, }, - [ChainId.JunoTestnet]: { - name: 'juno', - mainnet: false, - accentColor: '#f74a49', - factoryContractAddress: - 'juno1dacj3j6pwr7jx0jeu99qdc4a2ylc2rxp4v3zap54sfrl3ntrhe8qkjfpku', + }, + { + chainId: ChainId.JunoTestnet, + name: 'juno', + mainnet: false, + accentColor: '#f74a49', + factoryContractAddress: + 'juno1dacj3j6pwr7jx0jeu99qdc4a2ylc2rxp4v3zap54sfrl3ntrhe8qkjfpku', + indexes: { + search: 'testnet_daos', + featured: 'featured_daos', + }, + explorerUrlTemplates: { + tx: 'https://testnet.ping.pub/juno/tx/REPLACE', + gov: 'https://testnet.ping.pub/juno/gov', + govProp: 'https://testnet.ping.pub/juno/gov/REPLACE', + wallet: 'https://testnet.ping.pub/juno/account/REPLACE', + }, + gov: { supportsV1GovProposals: true, - indexes: { - search: 'testnet_daos', - featured: 'featured_daos', - }, - explorerUrlTemplates: { - tx: 'https://testnet.ping.pub/juno/tx/REPLACE', - gov: 'https://testnet.ping.pub/juno/gov', - govProp: 'https://testnet.ping.pub/juno/gov/REPLACE', - wallet: 'https://testnet.ping.pub/juno/account/REPLACE', - }, - codeIds: { - // https://github.com/CosmWasm/cw-plus - Cw4Group: 178, - // https://github.com/CosmWasm/cw-nfts - Cw721Base: 179, + }, + codeIds: { + // https://github.com/CosmWasm/cw-plus + Cw4Group: 178, + // https://github.com/CosmWasm/cw-nfts + Cw721Base: 179, - // ContractVersion.V230 - CwPayrollFactory: 3797, - CwTokenSwap: 3798, - CwTokenfactoryIssuer: 3799, - CwVesting: 3800, - DaoCore: 3801, - DaoMigrator: 3802, - DaoPreProposeMultiple: 3803, - DaoPreProposeSingle: 3804, - DaoProposalMultiple: 3805, - DaoProposalSingle: 3806, - DaoVotingCw4: 3807, - DaoVotingCw721Staked: 3808, - DaoVotingTokenStaked: 3809, + // ContractVersion.V230 + CwPayrollFactory: 3797, + CwTokenSwap: 3798, + CwTokenfactoryIssuer: 3799, + CwVesting: 3800, + DaoCore: 3801, + DaoMigrator: 3802, + DaoPreProposeMultiple: 3803, + DaoPreProposeSingle: 3804, + DaoProposalMultiple: 3805, + DaoProposalSingle: 3806, + DaoVotingCw4: 3807, + DaoVotingCw721Staked: 3808, + DaoVotingTokenStaked: 3809, - // v2.1.0 and below, for migrating v1 to v2 DAOs - // ContractVersion.V210 - Cw20Stake: 1247, - DaoVotingCw20Staked: 1263, - }, - historicalCodeIds: { - [ContractVersion.V210]: { - DaoPreProposeMultiple: 1258, - DaoProposalMultiple: 1261, - }, + // v2.1.0 and below, for migrating v1 to v2 DAOs + // ContractVersion.V210 + Cw20Stake: 1247, + DaoVotingCw20Staked: 1263, + }, + historicalCodeIds: { + [ContractVersion.V210]: { + DaoPreProposeMultiple: 1258, + DaoProposalMultiple: 1261, }, }, - [ChainId.OsmosisTestnet]: { - name: 'osmosis', - mainnet: false, - accentColor: '#5604e8', - factoryContractAddress: - 'osmo1v5k3527dt2vt67848h8jk0az9dyl8sunsqaapznf2j9tm4arxxfs7gwa0n', + }, + { + chainId: ChainId.OsmosisTestnet, + name: 'osmosis', + mainnet: false, + accentColor: '#5604e8', + factoryContractAddress: + 'osmo1v5k3527dt2vt67848h8jk0az9dyl8sunsqaapznf2j9tm4arxxfs7gwa0n', + indexes: { + search: 'osmosis_testnet_daos', + // Use same as mainnet. + featured: 'osmosis_featured_daos', + }, + explorerUrlTemplates: { + tx: 'https://testnet.ping.pub/osmosis/tx/REPLACE', + gov: 'https://testnet.ping.pub/osmosis/gov', + govProp: 'https://testnet.ping.pub/osmosis/gov/REPLACE', + wallet: 'https://testnet.ping.pub/osmosis/account/REPLACE', + }, + gov: { supportsV1GovProposals: false, - indexes: { - search: 'osmosis_testnet_daos', - // Use same as mainnet. - featured: 'osmosis_featured_daos', - }, - explorerUrlTemplates: { - tx: 'https://testnet.ping.pub/osmosis/tx/REPLACE', - gov: 'https://testnet.ping.pub/osmosis/gov', - govProp: 'https://testnet.ping.pub/osmosis/gov/REPLACE', - wallet: 'https://testnet.ping.pub/osmosis/account/REPLACE', - }, - codeIds: { - // https://github.com/CosmWasm/cw-plus - Cw4Group: 1327, // v0.16 - // https://github.com/CosmWasm/cw-nfts - Cw721Base: 1326, // v0.16 + }, + codeIds: { + // https://github.com/CosmWasm/cw-plus + Cw4Group: 1327, // v0.16 + // https://github.com/CosmWasm/cw-nfts + Cw721Base: 1326, // v0.16 - // ContractVersion.V210 - // https://github.com/DA0-DA0/dao-contracts/releases/tag/v2.1.0 - CwPayrollFactory: 4893, - CwTokenSwap: 4894, - CwTokenfactoryIssuer: 4895, - CwVesting: 4896, - DaoCore: 4897, - DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 4898 - DaoPreProposeMultiple: 4899, - DaoPreProposeSingle: 4900, - DaoProposalMultiple: 4901, - DaoProposalSingle: 4902, - DaoVotingCw4: 4903, - DaoVotingCw721Staked: 4904, - DaoVotingTokenStaked: 4905, - }, - historicalCodeIds: { - [ContractVersion.V210]: { - DaoPreProposeMultiple: 1319, - DaoProposalMultiple: 1322, - }, + // ContractVersion.V210 + // https://github.com/DA0-DA0/dao-contracts/releases/tag/v2.1.0 + CwPayrollFactory: 4893, + CwTokenSwap: 4894, + CwTokenfactoryIssuer: 4895, + CwVesting: 4896, + DaoCore: 4897, + DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 4898 + DaoPreProposeMultiple: 4899, + DaoPreProposeSingle: 4900, + DaoProposalMultiple: 4901, + DaoProposalSingle: 4902, + DaoVotingCw4: 4903, + DaoVotingCw721Staked: 4904, + DaoVotingTokenStaked: 4905, + }, + historicalCodeIds: { + [ContractVersion.V210]: { + DaoPreProposeMultiple: 1319, + DaoProposalMultiple: 1322, }, }, - [ChainId.StargazeTestnet]: { - name: 'stargaze', - mainnet: false, - accentColor: '#8ac3cc', - factoryContractAddress: - 'stars1ajrde5kky0c3xspjthqncxd72qmyu5trfsspn6ndk892gyqwakzsdjmegx', + }, + { + chainId: ChainId.StargazeTestnet, + name: 'stargaze', + mainnet: false, + accentColor: '#8ac3cc', + factoryContractAddress: + 'stars1ajrde5kky0c3xspjthqncxd72qmyu5trfsspn6ndk892gyqwakzsdjmegx', + indexes: { + search: 'stargaze_testnet_daos', + // Use same as mainnet. + featured: 'stargaze_featured_daos', + }, + explorerUrlTemplates: { + tx: 'https://testnet.ping.pub/stargaze/tx/REPLACE', + gov: 'https://testnet.ping.pub/stargaze/gov', + govProp: 'https://testnet.ping.pub/stargaze/gov/REPLACE', + wallet: 'https://testnet.ping.pub/stargaze/account/REPLACE', + }, + gov: { supportsV1GovProposals: false, - indexes: { - search: 'stargaze_testnet_daos', - // Use same as mainnet. - featured: 'stargaze_featured_daos', - }, - explorerUrlTemplates: { - tx: 'https://testnet.ping.pub/stargaze/tx/REPLACE', - gov: 'https://testnet.ping.pub/stargaze/gov', - govProp: 'https://testnet.ping.pub/stargaze/gov/REPLACE', - wallet: 'https://testnet.ping.pub/stargaze/account/REPLACE', - }, - codeIds: { - // https://github.com/CosmWasm/cw-plus - Cw4Group: 2887, // v0.16 + }, + codeIds: { + // https://github.com/CosmWasm/cw-plus + Cw4Group: 2887, // v0.16 - // ContractVersion.V230 - CwPayrollFactory: 3225, - CwTokenSwap: 3226, - CwTokenfactoryIssuer: 3227, - CwVesting: 3228, - DaoCore: 3229, - DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 3230 - DaoPreProposeMultiple: 3231, - DaoPreProposeSingle: 3232, - DaoProposalMultiple: 3233, - DaoProposalSingle: 3234, - DaoVotingCw4: 3235, - DaoVotingCw721Staked: 3236, - DaoVotingTokenStaked: 3237, - }, - historicalCodeIds: { - [ContractVersion.V210]: { - DaoPreProposeMultiple: 224, - DaoProposalMultiple: 226, - }, + // ContractVersion.V230 + CwPayrollFactory: 3225, + CwTokenSwap: 3226, + CwTokenfactoryIssuer: 3227, + CwVesting: 3228, + DaoCore: 3229, + DaoMigrator: -1, // not needed since only v2 DAOs exist but it's 3230 + DaoPreProposeMultiple: 3231, + DaoPreProposeSingle: 3232, + DaoProposalMultiple: 3233, + DaoProposalSingle: 3234, + DaoVotingCw4: 3235, + DaoVotingCw721Staked: 3236, + DaoVotingTokenStaked: 3237, + }, + historicalCodeIds: { + [ContractVersion.V210]: { + DaoPreProposeMultiple: 224, + DaoProposalMultiple: 226, }, }, - } + }, +] export const POLYTONE_CONFIG_PER_CHAIN: [ChainId, PolytoneConfig][] = - Object.entries(SUPPORTED_CHAINS).map(([chainId, { polytone = {} }]) => [ + SUPPORTED_CHAINS.map(({ chainId, polytone = {} }) => [ chainId as ChainId, polytone, ]) @@ -522,4 +543,29 @@ export const CHAIN_ENDPOINTS: Partial< rpc: 'https://rpc-kralum.neutron-1.neutron.org', rest: 'https://rest-kralum.neutron-1.neutron.org', }, + [ChainId.CosmosHubMainnet]: { + rpc: 'https://cosmos-rpc.polkachu.com', + rest: 'https://cosmos-api.polkachu.com', + }, } + +// All configured chains. Configured chains are either supported chains, which +// DAO DAO is deployed on, or other chains that show up in the governance UI. +export const CONFIGURED_CHAINS: BaseChainConfig[] = [ + { + chainId: ChainId.CosmosHubMainnet, + name: 'cosmos', + mainnet: true, + accentColor: '#5064fb', + gov: { + supportsV1GovProposals: false, + }, + explorerUrlTemplates: { + tx: 'https://ping.pub/cosmos/tx/REPLACE', + gov: 'https://ping.pub/cosmos/gov', + govProp: 'https://ping.pub/cosmos/gov/REPLACE', + wallet: 'https://ping.pub/cosmos/account/REPLACE', + }, + }, + ...SUPPORTED_CHAINS, +]