Skip to content

Commit

Permalink
finished migrating DAO info selectors to queries, and migrated entity…
Browse files Browse the repository at this point in the history
…/profile selectors to queries
  • Loading branch information
NoahSaso committed Jun 6, 2024
1 parent 36688e9 commit bc60440
Show file tree
Hide file tree
Showing 30 changed files with 1,112 additions and 1,151 deletions.
105 changes: 82 additions & 23 deletions packages/state/query/queries/chain.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { queryOptions } from '@tanstack/react-query'
import { QueryClient, queryOptions } from '@tanstack/react-query'

import { ModuleAccount } from '@dao-dao/types/protobuf/codegen/cosmos/auth/v1beta1/auth'
import {
cosmWasmClientRouter,
cosmosProtoRpcClientRouter,
isValidBech32Address,
} from '@dao-dao/utils'

/**
Expand Down Expand Up @@ -47,21 +48,16 @@ const fetchChainModuleAddress = async ({
}

/**
* Check whether or not the address is a chain module, optionally with a
* specific name.
* Fetch the module name associated with the specified address. Returns null if
* not a module account.
*/
export const isAddressModule = async ({
const fetchChainModuleName = async ({
chainId,
address,
moduleName,
}: {
chainId: string
address: string
/**
* If defined, check that the module address matches the specified name.
*/
moduleName?: string
}): Promise<boolean> => {
}): Promise<string | null> => {
const client = await cosmosProtoRpcClientRouter.connect(chainId)

try {
Expand All @@ -70,33 +66,84 @@ export const isAddressModule = async ({
})

if (!account) {
return false
return null
}

// If not decoded automatically...
if (account.typeUrl === ModuleAccount.typeUrl) {
const moduleAccount = ModuleAccount.decode(account.value)
return !moduleName || moduleAccount.name === moduleName
return ModuleAccount.decode(account.value).name

// If already decoded automatically.
// If decoded automatically...
} else if (account.$typeUrl === ModuleAccount.typeUrl) {
return (
!moduleName || (account as unknown as ModuleAccount).name === moduleName
)
return (account as unknown as ModuleAccount).name
}
} catch (err) {
// If no account found, return null.
if (
err instanceof Error &&
(err.message.includes('not found: key not found') ||
err.message.includes('decoding bech32 failed'))
err.message.includes('not found: key not found')
) {
return false
return null
}

// Rethrow other errors.
throw err
}

return false
return null
}

/**
* Check whether or not the address is a chain module, optionally with a
* specific name.
*/
export const isAddressModule = async (
queryClient: QueryClient,
{
chainId,
address,
moduleName,
}: {
chainId: string
address: string
/**
* If defined, check that the module address matches the specified name.
*/
moduleName?: string
}
): Promise<boolean> => {
if (!isValidBech32Address(address)) {
return false
}

try {
const name = await queryClient.fetchQuery(
chainQueries.moduleName({
chainId,
address,
})
)

// Null if not a module.
if (!name) {
return false
}

// If name to check provided, check it. Otherwise, return true.
return !moduleName || name === moduleName
} catch (err) {
// If invalid address, return false. Should never happen because of the
// check at the beginning, but just in case.
if (
err instanceof Error &&
err.message.includes('decoding bech32 failed')
) {
return false
}

// Rethrow other errors.
throw err
}
}

/**
Expand All @@ -122,14 +169,26 @@ export const chainQueries = {
queryKey: ['chain', 'moduleAddress', options],
queryFn: () => fetchChainModuleAddress(options),
}),
/**
* Fetch the module name associated with the specified address. Returns null
* if not a module account.
*/
moduleName: (options: Parameters<typeof fetchChainModuleName>[0]) =>
queryOptions({
queryKey: ['chain', 'moduleName', options],
queryFn: () => fetchChainModuleName(options),
}),
/**
* Check whether or not the address is a chain module, optionally with a
* specific name.
*/
isAddressModule: (options: Parameters<typeof isAddressModule>[0]) =>
isAddressModule: (
queryClient: QueryClient,
options: Parameters<typeof isAddressModule>[1]
) =>
queryOptions({
queryKey: ['chain', 'isAddressModule', options],
queryFn: () => isAddressModule(options),
queryFn: () => isAddressModule(queryClient, options),
}),
/**
* Fetch the timestamp for a given block height.
Expand Down
11 changes: 11 additions & 0 deletions packages/state/query/queries/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,17 @@ export const contractQueries = {
...options,
nameOrNames: ContractName.PolytoneProxy,
}),
/**
* Check if a contract is a cw1-whitelist.
*/
isCw1Whitelist: (
queryClient: QueryClient,
options: Omit<Parameters<typeof fetchIsContract>[1], 'nameOrNames'>
) =>
contractQueries.isContract(queryClient, {
...options,
nameOrNames: ContractName.Cw1Whitelist,
}),
/**
* Fetch contract instantiation time.
*/
Expand Down
132 changes: 132 additions & 0 deletions packages/state/query/queries/contracts/Cw1Whitelist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { QueryClient, UseQueryOptions } from '@tanstack/react-query'

import { AdminListResponse } from '@dao-dao/types/contracts/Cw1Whitelist'
import { cosmWasmClientRouter } from '@dao-dao/utils'

import { Cw1WhitelistQueryClient } from '../../../contracts/Cw1Whitelist'
import { contractQueries } from '../contract'
import { indexerQueries } from '../indexer'

export const cw1WhitelistQueryKeys = {
contract: [
{
contract: 'cw1Whitelist',
},
] as const,
address: (contractAddress: string) =>
[
{
...cw1WhitelistQueryKeys.contract[0],
address: contractAddress,
},
] as const,
adminList: (contractAddress: string, args?: Record<string, unknown>) =>
[
{
...cw1WhitelistQueryKeys.address(contractAddress)[0],
method: 'admin_list',
...(args && { args }),
},
] as const,
/**
* If this is a cw1-whitelist, return the admins. Otherwise, return null.
*/
adminsIfCw1Whitelist: (
contractAddress: string,
args?: Record<string, unknown>
) =>
[
{
...cw1WhitelistQueryKeys.address(contractAddress)[0],
method: 'adminsIfCw1Whitelist',
...(args && { args }),
},
] as const,
}
export const cw1WhitelistQueries = {
adminList: <TData = AdminListResponse>(
queryClient: QueryClient,
{ chainId, contractAddress, options }: Cw1WhitelistAdminListQuery<TData>
): UseQueryOptions<AdminListResponse, Error, TData> => ({
queryKey: cw1WhitelistQueryKeys.adminList(contractAddress),
queryFn: async () => {
let indexerNonExistent = false
try {
const adminList = await queryClient.fetchQuery(
indexerQueries.queryContract<AdminListResponse>(queryClient, {
chainId,
contractAddress,
formula: 'cw1Whitelist/adminList',
})
)
if (adminList) {
return adminList
} else {
indexerNonExistent = true
}
} catch (error) {
console.error(error)
}

if (indexerNonExistent) {
throw new Error('Admin list not found')
}

// If indexer query fails, fallback to contract query.
return new Cw1WhitelistQueryClient(
await cosmWasmClientRouter.connect(chainId),
contractAddress
).adminList()
},
...options,
}),
/**
* If this is a cw1-whitelist, return the admins. Otherwise, return null.
*/
adminsIfCw1Whitelist: <TData = string[] | null>(
queryClient: QueryClient,
{
chainId,
contractAddress,
options,
}: Cw1WhitelistAdminsIfCw1WhitelistQuery<TData>
): UseQueryOptions<string[] | null, Error, TData> => ({
queryKey: cw1WhitelistQueryKeys.adminsIfCw1Whitelist(contractAddress),
queryFn: async () => {
const isCw1Whitelist = await queryClient.fetchQuery(
contractQueries.isCw1Whitelist(queryClient, {
chainId,
address: contractAddress,
})
)
if (!isCw1Whitelist) {
return null
}

return (
await queryClient.fetchQuery(
cw1WhitelistQueries.adminList(queryClient, {
chainId,
contractAddress,
})
)
).admins
},
...options,
}),
}
export interface Cw1WhitelistReactQuery<TResponse, TData = TResponse> {
chainId: string
contractAddress: string
options?: Omit<
UseQueryOptions<TResponse, Error, TData>,
'queryKey' | 'queryFn' | 'initialData'
> & {
initialData?: undefined
}
}
export interface Cw1WhitelistAdminListQuery<TData>
extends Cw1WhitelistReactQuery<AdminListResponse, TData> {}

export interface Cw1WhitelistAdminsIfCw1WhitelistQuery<TData>
extends Cw1WhitelistReactQuery<string[] | null, TData> {}
1 change: 1 addition & 0 deletions packages/state/query/queries/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './Cw1Whitelist'
export * from './DaoDaoCore'
export * from './PolytoneNote'
export * from './PolytoneProxy'
Expand Down
1 change: 1 addition & 0 deletions packages/state/query/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './contract'
export * from './dao'
export * from './indexer'
export * from './polytone'
export * from './profile'
Loading

0 comments on commit bc60440

Please sign in to comment.