From 5123e74fb77721cda0f6b1d5b4a0ad05fb819d30 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 14 May 2024 11:09:37 +0700 Subject: [PATCH 1/7] feat(utils): move contracts service into new folder structure wasm --- src/lib/components/ContractSelectSection.tsx | 4 +- .../modal/contract/SaveNewContract.tsx | 2 +- .../select-contract/ContractListDetail.tsx | 2 +- .../select-contract/SelectContractAdmin.tsx | 2 +- .../SelectContractInstantiator.tsx | 2 +- src/lib/model/contract.ts | 2 +- src/lib/pages/account-details/data.ts | 4 +- src/lib/pages/admin/index.tsx | 2 +- src/lib/pages/code-details/data.ts | 4 +- .../components/CommandSection.tsx | 2 +- .../components/ContractTop.tsx | 2 +- .../components/InstantiateInfo.tsx | 2 +- .../contract-description/UserContractDesc.tsx | 2 +- .../components/contract-description/index.tsx | 2 +- .../components/tables/index.tsx | 2 +- src/lib/pages/contract-details/data.ts | 2 +- src/lib/pages/contracts/data.ts | 2 +- .../components/query-area/JsonQuery.tsx | 3 +- .../schema-query/SchemaQueryComponent.tsx | 2 +- src/lib/pages/interact-contract/index.tsx | 2 +- src/lib/pages/migrate/index.tsx | 2 +- src/lib/services/nameService.ts | 2 +- src/lib/services/searchService.ts | 2 +- src/lib/services/{ => types/wasm}/contract.ts | 167 +++--------------- src/lib/services/types/wasm/index.ts | 1 + src/lib/services/wasm/code/index.ts | 1 + src/lib/services/wasm/contract/api.ts | 147 +++++++++++++++ .../contract/index.ts} | 27 ++- src/lib/services/wasm/contract/lcd.ts | 0 29 files changed, 208 insertions(+), 188 deletions(-) rename src/lib/services/{ => types/wasm}/contract.ts (51%) create mode 100644 src/lib/services/wasm/contract/api.ts rename src/lib/services/{contractService.ts => wasm/contract/index.ts} (97%) create mode 100644 src/lib/services/wasm/contract/lcd.ts diff --git a/src/lib/components/ContractSelectSection.tsx b/src/lib/components/ContractSelectSection.tsx index 34666f44b..fa354c8d7 100644 --- a/src/lib/components/ContractSelectSection.tsx +++ b/src/lib/components/ContractSelectSection.tsx @@ -5,8 +5,8 @@ import { useForm } from "react-hook-form"; import { useMobile } from "lib/app-provider"; import { useContractStore } from "lib/providers/store"; -import type { ContractDetail } from "lib/services/contractService"; -import { useContractDetailByContractAddress } from "lib/services/contractService"; +import type { ContractDetail } from "lib/services/types"; +import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { BechAddr, BechAddr32, Option } from "lib/types"; diff --git a/src/lib/components/modal/contract/SaveNewContract.tsx b/src/lib/components/modal/contract/SaveNewContract.tsx index e20f588e0..f4af1234b 100644 --- a/src/lib/components/modal/contract/SaveNewContract.tsx +++ b/src/lib/components/modal/contract/SaveNewContract.tsx @@ -13,7 +13,7 @@ import { OffChainForm } from "lib/components/OffChainForm"; import { INSTANTIATED_LIST_NAME } from "lib/data"; import { useHandleContractSave } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; -import { useContractDetailByContractAddress } from "lib/services/contractService"; +import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; import type { BechAddr, BechAddr32, LVPair } from "lib/types"; import { formatSlugName, diff --git a/src/lib/components/select-contract/ContractListDetail.tsx b/src/lib/components/select-contract/ContractListDetail.tsx index 4b123c47c..65530c35f 100644 --- a/src/lib/components/select-contract/ContractListDetail.tsx +++ b/src/lib/components/select-contract/ContractListDetail.tsx @@ -8,7 +8,7 @@ import { useCurrentChain } from "lib/app-provider"; import { DisconnectedState, EmptyState, ZeroState } from "lib/components/state"; import { TagSelection } from "lib/components/TagSelection"; import { INSTANTIATED_LIST_NAME } from "lib/data"; -import { useAdminByContractAddresses } from "lib/services/contractService"; +import { useAdminByContractAddresses } from "lib/services/wasm/contract"; import type { ContractListInfo } from "lib/stores/contract"; import type { BechAddr32, ContractInfo } from "lib/types"; import { formatSlugName } from "lib/utils"; diff --git a/src/lib/components/select-contract/SelectContractAdmin.tsx b/src/lib/components/select-contract/SelectContractAdmin.tsx index 7b2fa4955..1f69ee516 100644 --- a/src/lib/components/select-contract/SelectContractAdmin.tsx +++ b/src/lib/components/select-contract/SelectContractAdmin.tsx @@ -15,7 +15,7 @@ import { useCurrentChain } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { ADMIN_SPECIAL_SLUG } from "lib/data"; import { useContractStore } from "lib/providers/store"; -import { useContractListByAdmin } from "lib/services/contractService"; +import { useContractListByAdmin } from "lib/services/wasm/contract"; import type { ContractListInfo, ContractLocalInfo } from "lib/stores/contract"; import type { BechAddr32 } from "lib/types"; import { getCurrentDate } from "lib/utils"; diff --git a/src/lib/components/select-contract/SelectContractInstantiator.tsx b/src/lib/components/select-contract/SelectContractInstantiator.tsx index 0b4eacf5c..62ebe7b2c 100644 --- a/src/lib/components/select-contract/SelectContractInstantiator.tsx +++ b/src/lib/components/select-contract/SelectContractInstantiator.tsx @@ -30,7 +30,7 @@ import { import { DEFAULT_RPC_ERROR } from "lib/data"; import { useInstantiatedByMe } from "lib/model/contract"; import { useContractStore } from "lib/providers/store"; -import { queryContract } from "lib/services/contract"; +import { queryContract } from "lib/services/wasm/contract"; import type { BechAddr32, RpcQueryError } from "lib/types"; import { AllContractLists } from "./AllContractLists"; diff --git a/src/lib/model/contract.ts b/src/lib/model/contract.ts index 374fa8d2c..b5157209d 100644 --- a/src/lib/model/contract.ts +++ b/src/lib/model/contract.ts @@ -4,7 +4,7 @@ import { useContractStore } from "lib/providers/store"; import { useInstantiatedCountByUserQuery, useInstantiatedListByUserQuery, -} from "lib/services/contractService"; +} from "lib/services/wasm/contract"; import type { ContractListInfo } from "lib/stores/contract"; import type { BechAddr, BechAddr32 } from "lib/types"; import { formatSlugName, getCurrentDate, getDefaultDate } from "lib/utils"; diff --git a/src/lib/pages/account-details/data.ts b/src/lib/pages/account-details/data.ts index c23f36e1e..21c6422c1 100644 --- a/src/lib/pages/account-details/data.ts +++ b/src/lib/pages/account-details/data.ts @@ -1,11 +1,11 @@ import { useCodeStore, useContractStore } from "lib/providers/store"; import { useAccountTableCounts } from "lib/services/accountService"; import { useBalances } from "lib/services/bank"; +import { useCodesByAddress } from "lib/services/wasm/code"; import { useAdminContractsByAddress, useInstantiatedContractsByAddress, -} from "lib/services/contractService"; -import { useCodesByAddress } from "lib/services/wasm/code"; +} from "lib/services/wasm/contract"; import type { BechAddr, CodeInfo, diff --git a/src/lib/pages/admin/index.tsx b/src/lib/pages/admin/index.tsx index 59f246614..e9f828f3b 100644 --- a/src/lib/pages/admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -23,7 +23,7 @@ import { TextInput } from "lib/components/forms"; import { UserDocsLink } from "lib/components/UserDocsLink"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useTxBroadcast } from "lib/hooks"; -import { useContractDetailByContractAddress } from "lib/services/contractService"; +import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; import type { BechAddr, BechAddr32 } from "lib/types"; import { MsgType } from "lib/types"; import { composeMsg, getFirstQueryParam } from "lib/utils"; diff --git a/src/lib/pages/code-details/data.ts b/src/lib/pages/code-details/data.ts index a0474fb90..fed9022aa 100644 --- a/src/lib/pages/code-details/data.ts +++ b/src/lib/pages/code-details/data.ts @@ -1,8 +1,8 @@ import type { UseQueryOptions } from "@tanstack/react-query"; import { useContractStore } from "lib/providers/store"; -import type { ContractsResponse } from "lib/services/contract"; -import { useContractsByCodeId } from "lib/services/contractService"; +import type { ContractsResponse } from "lib/services/types"; +import { useContractsByCodeId } from "lib/services/wasm/contract"; import type { ContractInfo, Option } from "lib/types"; export const useCodeContracts = ( diff --git a/src/lib/pages/contract-details/components/CommandSection.tsx b/src/lib/pages/contract-details/components/CommandSection.tsx index df6c1491f..6a5f1afb5 100644 --- a/src/lib/pages/contract-details/components/CommandSection.tsx +++ b/src/lib/pages/contract-details/components/CommandSection.tsx @@ -21,7 +21,7 @@ import { import { Tooltip } from "lib/components/Tooltip"; import { useExecuteCmds } from "lib/hooks"; import { useSchemaStore } from "lib/providers/store"; -import { useContractQueryMsgs } from "lib/services/contractService"; +import { useContractQueryMsgs } from "lib/services/wasm/contract"; import type { BechAddr32 } from "lib/types"; import { encode, jsonPrettify } from "lib/utils"; diff --git a/src/lib/pages/contract-details/components/ContractTop.tsx b/src/lib/pages/contract-details/components/ContractTop.tsx index 311688fbb..d3fc89dc6 100644 --- a/src/lib/pages/contract-details/components/ContractTop.tsx +++ b/src/lib/pages/contract-details/components/ContractTop.tsx @@ -20,7 +20,7 @@ import { SaveContractDetailsModal, } from "lib/components/modal"; import { TotalValue } from "lib/components/TotalValue"; -import type { Contract } from "lib/services/contract"; +import type { Contract } from "lib/services/types"; import type { ContractLocalInfo } from "lib/stores/contract"; import { ContractInteractionTabs } from "lib/types"; import type { diff --git a/src/lib/pages/contract-details/components/InstantiateInfo.tsx b/src/lib/pages/contract-details/components/InstantiateInfo.tsx index 36f50dca9..66099680b 100644 --- a/src/lib/pages/contract-details/components/InstantiateInfo.tsx +++ b/src/lib/pages/contract-details/components/InstantiateInfo.tsx @@ -4,7 +4,7 @@ import { useCurrentChain, useGetAddressType } from "lib/app-provider"; import { Copier } from "lib/components/copy"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { LabelText } from "lib/components/LabelText"; -import type { Contract, ContractRest } from "lib/services/contract"; +import type { Contract, ContractRest } from "lib/services/types"; import type { CodeLocalInfo } from "lib/stores/code"; import type { Nullable, Option } from "lib/types"; import { dateFromNow, formatUTC, getCw2Info } from "lib/utils"; diff --git a/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx index 60a786236..d07d3945d 100644 --- a/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx +++ b/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx @@ -6,7 +6,7 @@ import { useClampText } from "use-clamp-text"; import { ShowMoreButton } from "lib/components/button"; import { CustomIcon } from "lib/components/icon"; import { EditContractDetailsModal } from "lib/components/modal"; -import type { Contract } from "lib/services/contract"; +import type { Contract } from "lib/services/types"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { Nullable, Option, PublicContractInfo } from "lib/types"; diff --git a/src/lib/pages/contract-details/components/contract-description/index.tsx b/src/lib/pages/contract-details/components/contract-description/index.tsx index ab40c996e..38ad68acd 100644 --- a/src/lib/pages/contract-details/components/contract-description/index.tsx +++ b/src/lib/pages/contract-details/components/contract-description/index.tsx @@ -3,7 +3,7 @@ import { Flex } from "@chakra-ui/react"; import { useMobile } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { PublicDescription } from "lib/components/PublicDescription"; -import type { Contract } from "lib/services/contract"; +import type { Contract } from "lib/services/types"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { Nullable, Option, PublicContractInfo } from "lib/types"; diff --git a/src/lib/pages/contract-details/components/tables/index.tsx b/src/lib/pages/contract-details/components/tables/index.tsx index a7d4be7a7..421e65d37 100644 --- a/src/lib/pages/contract-details/components/tables/index.tsx +++ b/src/lib/pages/contract-details/components/tables/index.tsx @@ -9,7 +9,7 @@ import { import { useGovConfig } from "lib/app-provider"; import { CustomTab } from "lib/components/CustomTab"; -import { useContractTableCounts } from "lib/services/contractService"; +import { useContractTableCounts } from "lib/services/wasm/contract"; import type { BechAddr32 } from "lib/types"; import { MigrationTable } from "./migration"; diff --git a/src/lib/pages/contract-details/data.ts b/src/lib/pages/contract-details/data.ts index f7c2965c9..95250a9ba 100644 --- a/src/lib/pages/contract-details/data.ts +++ b/src/lib/pages/contract-details/data.ts @@ -2,7 +2,7 @@ import { useCodeStore, useContractStore } from "lib/providers/store"; import { useContractDataByContractAddress, useMigrationHistoriesByContractAddress, -} from "lib/services/contractService"; +} from "lib/services/wasm/contract"; import type { BechAddr32, ContractMigrationHistory } from "lib/types"; export const useContractData = (contractAddress: BechAddr32) => { diff --git a/src/lib/pages/contracts/data.ts b/src/lib/pages/contracts/data.ts index f0205f680..a717d5eca 100644 --- a/src/lib/pages/contracts/data.ts +++ b/src/lib/pages/contracts/data.ts @@ -1,5 +1,5 @@ import { useContractStore } from "lib/providers/store"; -import { useContracts } from "lib/services/contractService"; +import { useContracts } from "lib/services/wasm/contract"; import type { ContractInfo } from "lib/types"; export const useRecentContracts = ( diff --git a/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx b/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx index 8c5824b81..718dbbb38 100644 --- a/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx +++ b/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx @@ -18,8 +18,7 @@ import JsonReadOnly from "lib/components/json/JsonReadOnly"; import { LoadingOverlay } from "lib/components/LoadingOverlay"; import { DEFAULT_RPC_ERROR } from "lib/data"; import { useContractStore } from "lib/providers/store"; -import { queryData } from "lib/services/contract"; -import { useContractQueryMsgs } from "lib/services/contractService"; +import { queryData, useContractQueryMsgs } from "lib/services/wasm/contract"; import type { BechAddr32, RpcQueryError } from "lib/types"; import { encode, diff --git a/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx b/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx index f6b524bf4..3a32dc157 100644 --- a/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx +++ b/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx @@ -27,7 +27,7 @@ import { OutputMessageTabs, } from "lib/components/json-schema"; import { DEFAULT_RPC_ERROR } from "lib/data"; -import { queryData } from "lib/services/contract"; +import { queryData } from "lib/services/wasm/contract"; import type { Activity } from "lib/stores/contract"; import type { SchemaInfo } from "lib/stores/schema"; import type { diff --git a/src/lib/pages/interact-contract/index.tsx b/src/lib/pages/interact-contract/index.tsx index 037b6473f..f10270191 100644 --- a/src/lib/pages/interact-contract/index.tsx +++ b/src/lib/pages/interact-contract/index.tsx @@ -12,7 +12,7 @@ import { ContractSelectSection } from "lib/components/ContractSelectSection"; import PageContainer from "lib/components/PageContainer"; import { InvalidState } from "lib/components/state"; import { UserDocsLink } from "lib/components/UserDocsLink"; -import type { ContractDetail } from "lib/services/contractService"; +import type { ContractDetail } from "lib/services/types"; import { ContractInteractionTabs } from "lib/types"; import type { BechAddr32, Coin } from "lib/types"; import { jsonPrettify, jsonValidate, libDecode } from "lib/utils"; diff --git a/src/lib/pages/migrate/index.tsx b/src/lib/pages/migrate/index.tsx index 2b5398ae5..d76a2e360 100644 --- a/src/lib/pages/migrate/index.tsx +++ b/src/lib/pages/migrate/index.tsx @@ -17,8 +17,8 @@ import { Loading } from "lib/components/Loading"; import { Stepper } from "lib/components/stepper"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useUploadCode } from "lib/hooks"; -import { useContractDetailByContractAddress } from "lib/services/contractService"; import { useUploadAccessParams } from "lib/services/proposalService"; +import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; import type { BechAddr32 } from "lib/types"; import { getFirstQueryParam } from "lib/utils"; diff --git a/src/lib/services/nameService.ts b/src/lib/services/nameService.ts index 8e6f0e61f..c8b046c58 100644 --- a/src/lib/services/nameService.ts +++ b/src/lib/services/nameService.ts @@ -13,9 +13,9 @@ import { } from "lib/app-provider"; import type { BechAddr, BechAddr32, Option } from "lib/types"; -import { queryContract } from "./contract"; import type { ICNSNamesResponse } from "./ns"; import { queryAddressByICNSName, queryICNSNamesByAddress } from "./ns"; +import { queryContract } from "./wasm/contract"; export const useICNSNamesByAddress = ( address: Option diff --git a/src/lib/services/searchService.ts b/src/lib/services/searchService.ts index 047a63b84..d953ff263 100644 --- a/src/lib/services/searchService.ts +++ b/src/lib/services/searchService.ts @@ -20,7 +20,6 @@ import { } from "lib/utils"; import { useBlockData } from "./block"; -import { queryContract } from "./contract"; import { useModuleByAddressLcd } from "./move/moduleService"; import { useAddressByICNSName, useICNSNamesByAddress } from "./nameService"; import type { ICNSNamesResponse } from "./ns"; @@ -29,6 +28,7 @@ import { useProposalData } from "./proposalService"; import { useTxData } from "./tx"; import { useValidatorData } from "./validator"; import { useCodeDataByCodeId } from "./wasm/code"; +import { queryContract } from "./wasm/contract"; export type SearchResultType = | "Code ID" diff --git a/src/lib/services/contract.ts b/src/lib/services/types/wasm/contract.ts similarity index 51% rename from src/lib/services/contract.ts rename to src/lib/services/types/wasm/contract.ts index 670191293..1e670762b 100644 --- a/src/lib/services/contract.ts +++ b/src/lib/services/types/wasm/contract.ts @@ -1,12 +1,6 @@ -import axios from "axios"; -import { z } from "zod"; +import z from "zod"; -import type { - BechAddr, - BechAddr32, - ContractInfo, - ContractMigrationHistory, -} from "lib/types"; +import type { ContractLocalInfo } from "lib/stores/contract"; import { zBechAddr, zBechAddr32, @@ -15,14 +9,26 @@ import { zPublicContractInfo, zUtcDate, } from "lib/types"; -import { encode, parseTxHash, parseWithError, snakeToCamel } from "lib/utils"; +import type { + BechAddr, + ContractInfo, + ContractMigrationHistory, + Option, +} from "lib/types"; +import { parseTxHash, snakeToCamel } from "lib/utils"; + +export interface ContractDetail extends ContractLocalInfo { + codeId: number; + codeHash: string; + admin: Option; +} export interface ContractCw2Info { contract: string; version: string; } -const zContractRest = z.object({ +export const zContractRest = z.object({ address: zBechAddr32, contract_info: z.object({ code_id: z.string(), @@ -41,26 +47,6 @@ const zContractRest = z.object({ }); export type ContractRest = z.infer; -export const queryData = async ( - endpoint: string, - contractAddress: BechAddr32, - msg: string -) => { - const b64 = encode(msg); - const { data } = await axios.get( - `${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${b64}` - ); - return data; -}; - -export const queryContract = async ( - endpoint: string, - contractAddress: BechAddr32 -) => - axios(`${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}`).then( - ({ data }) => parseWithError(zContractRest, data) - ); - const zContractsResponseItem = z .object({ contract_address: zBechAddr32, @@ -82,60 +68,13 @@ const zContractsResponseItem = z remark: val.remark, })); -const zContractsResponse = z.object({ +export const zContractsResponse = z.object({ items: z.array(zContractsResponseItem), total: z.number().nonnegative(), }); export type ContractsResponse = z.infer; -export const getContracts = async ( - endpoint: string, - limit: number, - offset: number -) => - axios - .get(`${endpoint}`, { - params: { - limit, - offset, - }, - }) - .then(({ data }) => parseWithError(zContractsResponse, data)); - -export const getInstantiatedContractsByAddress = async ( - endpoint: string, - address: BechAddr, - limit: number, - offset: number -) => - axios - .get( - `${endpoint}/${encodeURIComponent(address)}/wasm/instantiated-contracts`, - { - params: { - limit, - offset, - }, - } - ) - .then(({ data }) => parseWithError(zContractsResponse, data)); - -export const getAdminContractsByAddress = async ( - endpoint: string, - address: BechAddr, - limit: number, - offset: number -) => - axios - .get(`${endpoint}/${encodeURIComponent(address)}/wasm/admin-contracts`, { - params: { - limit, - offset, - }, - }) - .then(({ data }) => parseWithError(zContractsResponse, data)); - export const zContract = z .object({ address: zBechAddr32, @@ -156,7 +95,7 @@ export const zContract = z .transform(snakeToCamel); export type Contract = z.infer; -const zContractData = z +export const zContractData = z .object({ project_info: zProjectInfo.nullable(), public_info: zPublicContractInfo.nullable(), @@ -171,20 +110,7 @@ const zContractData = z })); export type ContractData = z.infer; -export const getContractDataByContractAddress = async ( - endpoint: string, - contractAddress: BechAddr32, - isGov: boolean -) => - axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/info`, { - params: { - is_gov: isGov, - }, - }) - .then(({ data }) => parseWithError(zContractData, data)); - -const zContractTableCounts = z +export const zContractTableCounts = z .object({ tx: z.number().nullish(), migration: z.number().nullish(), @@ -193,19 +119,6 @@ const zContractTableCounts = z .transform(snakeToCamel); export type ContractTableCounts = z.infer; -export const getContractTableCounts = async ( - endpoint: string, - contractAddress: BechAddr32, - isGov: boolean -): Promise => - axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/table-counts`, { - params: { - is_gov: isGov, - }, - }) - .then(({ data }) => parseWithError(zContractTableCounts, data)); - const zMigrationHistoriesResponseItem = z .object({ code_id: z.number().positive(), @@ -227,7 +140,7 @@ const zMigrationHistoriesResponseItem = z timestamp: val.timestamp, uploader: val.uploader, })); -const zMigrationHistoriesResponse = z.object({ +export const zMigrationHistoriesResponse = z.object({ items: z.array(zMigrationHistoriesResponseItem), }); @@ -235,48 +148,10 @@ export type MigrationHistoriesResponse = z.infer< typeof zMigrationHistoriesResponse >; -export const getMigrationHistoriesByContractAddress = async ( - endpoint: string, - contractAddress: BechAddr32, - limit: number, - offset: number -) => - axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/migrations`, { - params: { - limit, - offset, - }, - }) - .then(({ data }) => parseWithError(zMigrationHistoriesResponse, data)); - -const zContractQueryMsgs = z +export const zContractQueryMsgs = z .object({ query: z.array(z.string()), }) .transform((val) => val.query.map<[string, string]>((msg) => [msg, `{"${msg}": {}}`]) ); - -export const getContractQueryMsgs = async ( - endpoint: string, - contractAddress: BechAddr32 -) => - axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/query-msgs`) - .then(({ data }) => parseWithError(zContractQueryMsgs, data)); - -export const getContractsByCodeId = async ( - endpoint: string, - codeId: number, - limit: number, - offset: number -): Promise => - axios - .get(`${endpoint}/${codeId}/contracts`, { - params: { - limit, - offset, - }, - }) - .then(({ data }) => parseWithError(zContractsResponse, data)); diff --git a/src/lib/services/types/wasm/index.ts b/src/lib/services/types/wasm/index.ts index dbba02eb2..64f1def6b 100644 --- a/src/lib/services/types/wasm/index.ts +++ b/src/lib/services/types/wasm/index.ts @@ -1 +1,2 @@ export * from "./code"; +export * from "./contract"; diff --git a/src/lib/services/wasm/code/index.ts b/src/lib/services/wasm/code/index.ts index 1b076a8f4..9a58131fa 100644 --- a/src/lib/services/wasm/code/index.ts +++ b/src/lib/services/wasm/code/index.ts @@ -95,3 +95,4 @@ export const useCodesByAddress = ( }; export * from "./gql"; +export * from "./lcd"; diff --git a/src/lib/services/wasm/contract/api.ts b/src/lib/services/wasm/contract/api.ts new file mode 100644 index 000000000..b39fb8b58 --- /dev/null +++ b/src/lib/services/wasm/contract/api.ts @@ -0,0 +1,147 @@ +import axios from "axios"; + +import { + zContractData, + zContractQueryMsgs, + zContractRest, + zContractsResponse, + zContractTableCounts, + zMigrationHistoriesResponse, +} from "lib/services/types/"; +import type { + ContractsResponse, + ContractTableCounts, +} from "lib/services/types/"; +import type { BechAddr, BechAddr32 } from "lib/types"; +import { encode, parseWithError } from "lib/utils"; + +export const queryData = async ( + endpoint: string, + contractAddress: BechAddr32, + msg: string +) => { + const b64 = encode(msg); + const { data } = await axios.get( + `${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${b64}` + ); + return data; +}; + +export const queryContract = async ( + endpoint: string, + contractAddress: BechAddr32 +) => + axios(`${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}`).then( + ({ data }) => parseWithError(zContractRest, data) + ); + +export const getContracts = async ( + endpoint: string, + limit: number, + offset: number +) => + axios + .get(`${endpoint}`, { + params: { + limit, + offset, + }, + }) + .then(({ data }) => parseWithError(zContractsResponse, data)); + +export const getInstantiatedContractsByAddress = async ( + endpoint: string, + address: BechAddr, + limit: number, + offset: number +) => + axios + .get( + `${endpoint}/${encodeURIComponent(address)}/wasm/instantiated-contracts`, + { + params: { + limit, + offset, + }, + } + ) + .then(({ data }) => parseWithError(zContractsResponse, data)); + +export const getAdminContractsByAddress = async ( + endpoint: string, + address: BechAddr, + limit: number, + offset: number +) => + axios + .get(`${endpoint}/${encodeURIComponent(address)}/wasm/admin-contracts`, { + params: { + limit, + offset, + }, + }) + .then(({ data }) => parseWithError(zContractsResponse, data)); + +export const getContractDataByContractAddress = async ( + endpoint: string, + contractAddress: BechAddr32, + isGov: boolean +) => + axios + .get(`${endpoint}/${encodeURIComponent(contractAddress)}/info`, { + params: { + is_gov: isGov, + }, + }) + .then(({ data }) => parseWithError(zContractData, data)); + +export const getContractTableCounts = async ( + endpoint: string, + contractAddress: BechAddr32, + isGov: boolean +): Promise => + axios + .get(`${endpoint}/${encodeURIComponent(contractAddress)}/table-counts`, { + params: { + is_gov: isGov, + }, + }) + .then(({ data }) => parseWithError(zContractTableCounts, data)); + +export const getMigrationHistoriesByContractAddress = async ( + endpoint: string, + contractAddress: BechAddr32, + limit: number, + offset: number +) => + axios + .get(`${endpoint}/${encodeURIComponent(contractAddress)}/migrations`, { + params: { + limit, + offset, + }, + }) + .then(({ data }) => parseWithError(zMigrationHistoriesResponse, data)); + +export const getContractQueryMsgs = async ( + endpoint: string, + contractAddress: BechAddr32 +) => + axios + .get(`${endpoint}/${encodeURIComponent(contractAddress)}/query-msgs`) + .then(({ data }) => parseWithError(zContractQueryMsgs, data)); + +export const getContractsByCodeId = async ( + endpoint: string, + codeId: number, + limit: number, + offset: number +): Promise => + axios + .get(`${endpoint}/${codeId}/contracts`, { + params: { + limit, + offset, + }, + }) + .then(({ data }) => parseWithError(zContractsResponse, data)); diff --git a/src/lib/services/contractService.ts b/src/lib/services/wasm/contract/index.ts similarity index 97% rename from src/lib/services/contractService.ts rename to src/lib/services/wasm/contract/index.ts index 82fc91b70..47d46d6d9 100644 --- a/src/lib/services/contractService.ts +++ b/src/lib/services/wasm/contract/index.ts @@ -1,7 +1,8 @@ -import type { UseQueryOptions, UseQueryResult } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult } from "@tanstack/react-query"; import { useCallback } from "react"; +import { getCodeIdInfoLcd } from "../code"; import { CELATONE_QUERY_KEYS, useBaseApiRoute, @@ -16,6 +17,13 @@ import { getInstantiatedCountByUserQueryDocument, getInstantiatedListByUserQueryDocument, } from "lib/query"; +import type { + ContractData, + ContractDetail, + ContractsResponse, + ContractTableCounts, + MigrationHistoriesResponse, +} from "lib/services/types"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { BechAddr, BechAddr20, BechAddr32, Dict, Option } from "lib/types"; @@ -28,20 +36,7 @@ import { getContractTableCounts, getInstantiatedContractsByAddress, getMigrationHistoriesByContractAddress, -} from "./contract"; -import type { - ContractData, - ContractsResponse, - ContractTableCounts, - MigrationHistoriesResponse, -} from "./contract"; -import { getCodeIdInfoLcd } from "./wasm/code/lcd"; - -export interface ContractDetail extends ContractLocalInfo { - codeId: number; - codeHash: string; - admin: Option; -} +} from "./api"; export const useContractDetailByContractAddress = ( contractAddress: BechAddr32, @@ -370,3 +365,5 @@ export const useContractsByCodeId = ( } ); }; + +export * from "./api"; diff --git a/src/lib/services/wasm/contract/lcd.ts b/src/lib/services/wasm/contract/lcd.ts new file mode 100644 index 000000000..e69de29bb From 133c5cb6e8cd7e60ed8fd4eb9c722cdeb8416e86 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 14 May 2024 11:16:04 +0700 Subject: [PATCH 2/7] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7575df21..bc2bea634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#925](https://github.com/alleslabs/celatone-frontend/pull/925) Move contracts service to new folder structure - [#919](https://github.com/alleslabs/celatone-frontend/pull/919) Remove singleStakingDenom config and use from lcd instead - [#918](https://github.com/alleslabs/celatone-frontend/pull/918) Support lite version for delegation informations - [#916](https://github.com/alleslabs/celatone-frontend/pull/916) Support lite version balances From aec3dcefd5035e7225b2216f0612f16aa38e990b Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 14 May 2024 11:23:05 +0700 Subject: [PATCH 3/7] feat(utils): remove lcd.ts --- src/lib/services/wasm/contract/lcd.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/lib/services/wasm/contract/lcd.ts diff --git a/src/lib/services/wasm/contract/lcd.ts b/src/lib/services/wasm/contract/lcd.ts deleted file mode 100644 index e69de29bb..000000000 From 857d8eb0a99f74ee70c379598a0c606a2e2b12e0 Mon Sep 17 00:00:00 2001 From: Songwong Tasneeyapant <16089160+songwongtp@users.noreply.github.com> Date: Tue, 14 May 2024 11:35:48 +0700 Subject: [PATCH 4/7] fix: update index.ts --- src/lib/services/wasm/contract/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/services/wasm/contract/index.ts b/src/lib/services/wasm/contract/index.ts index 47d46d6d9..8ab3cabb0 100644 --- a/src/lib/services/wasm/contract/index.ts +++ b/src/lib/services/wasm/contract/index.ts @@ -365,5 +365,3 @@ export const useContractsByCodeId = ( } ); }; - -export * from "./api"; From b83ffe693a724bc6c18123846a2c19db22e0410c Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Tue, 14 May 2024 15:48:33 +0700 Subject: [PATCH 5/7] feat: restructure contract services --- src/lib/app-provider/env.ts | 5 +- src/lib/components/ContractSelectSection.tsx | 19 +- .../modal/contract/SaveNewContract.tsx | 21 +- .../SelectContractInstantiator.tsx | 43 ++-- src/lib/pages/admin/index.tsx | 13 +- src/lib/pages/contract-details/data.ts | 6 +- src/lib/pages/contract-details/index.tsx | 4 +- .../components/query-area/JsonQuery.tsx | 68 ++--- .../schema-query/SchemaQueryComponent.tsx | 90 +++---- .../schema-query/SchemaQueryResponse.tsx | 6 +- .../query-area/schema-query/index.tsx | 4 +- src/lib/pages/interact-contract/index.tsx | 20 +- src/lib/pages/migrate/index.tsx | 17 +- src/lib/services/nameService.ts | 4 +- src/lib/services/searchService.ts | 25 +- src/lib/services/types/wasm/contract.ts | 56 ++-- src/lib/services/wasm/contract/api.ts | 33 +-- src/lib/services/wasm/contract/gql.ts | 154 +++++++++++ src/lib/services/wasm/contract/index.ts | 242 +++--------------- src/lib/services/wasm/contract/lcd.ts | 21 ++ 20 files changed, 402 insertions(+), 449 deletions(-) create mode 100644 src/lib/services/wasm/contract/gql.ts create mode 100644 src/lib/services/wasm/contract/lcd.ts diff --git a/src/lib/app-provider/env.ts b/src/lib/app-provider/env.ts index ece4bae5d..ed1d840ed 100644 --- a/src/lib/app-provider/env.ts +++ b/src/lib/app-provider/env.ts @@ -13,10 +13,9 @@ export enum CELATONE_QUERY_KEYS { // CONTRACT,CODE LCD CODE_INFO = "CELATONE_QUERY_CODE_INFO", CODE_INFO_LCD = "CELATONE_QUERY_CODE_INFO_LCD", - CONTRACT_DETAIL_BY_CONTRACT_ADDRESS = "CELATONE_QUERY_CONTRACT_DETAIL_BY_CONTRACT_ADDRESS", - CONTRACT_INFO = "CELATONE_QUERY_CONTRACT_INFO", + CONTRACT_LCD = "CELATONE_QUERY_CONTRACT_LCD", CONTRACT_QUERY_MSGS = "CELATONE_QUERY_CONTRACT_QUERY_MSGS", - CONTRACT_QUERY = "CELATONE_QUERY_CONTRACT_QUERY", + CONTRACT_QUERY_LCD = "CELATONE_QUERY_CONTRACT_QUERY_LCD", CONTRACT_STATE = "CELATONE_QUERY_CONTRACT_STATE", CONTRACTS_BY_CODE_ID = "CELATONE_QUERY_CONTRACTS_BY_CODE_ID", // ACCOUNT diff --git a/src/lib/components/ContractSelectSection.tsx b/src/lib/components/ContractSelectSection.tsx index fa354c8d7..d3b359540 100644 --- a/src/lib/components/ContractSelectSection.tsx +++ b/src/lib/components/ContractSelectSection.tsx @@ -5,8 +5,8 @@ import { useForm } from "react-hook-form"; import { useMobile } from "lib/app-provider"; import { useContractStore } from "lib/providers/store"; -import type { ContractDetail } from "lib/services/types"; -import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; +import type { ContractLcd } from "lib/services/types"; +import { useContractLcd } from "lib/services/wasm/contract"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { BechAddr, BechAddr32, Option } from "lib/types"; @@ -37,7 +37,7 @@ interface ContractSelectSectionProps { mode: "all-lists" | "only-admin"; contractAddress: BechAddr32; onContractSelect: (contract: BechAddr32) => void; - successCallback?: (data: ContractDetail) => void; + successCallback?: (data: ContractLcd) => void; } const modeStyle = (mode: string) => { @@ -157,18 +157,17 @@ export const ContractSelectSection = observer( mode: "all", }); - const { refetch, isFetching } = useContractDetailByContractAddress( - contractAddress, - (data) => { + const { refetch, isFetching } = useContractLcd(contractAddress, { + onSuccess: (data) => { successCallback?.(data); reset({ isValid: true, - instantiator: data.instantiator, - label: data.label, + instantiator: data.contract.instantiator, + label: data.contract.label, }); }, - () => reset(defaultValues) - ); + onError: () => reset(defaultValues), + }); useEffect(() => { if (!contractLocalInfo) { diff --git a/src/lib/components/modal/contract/SaveNewContract.tsx b/src/lib/components/modal/contract/SaveNewContract.tsx index f4af1234b..7eafacb27 100644 --- a/src/lib/components/modal/contract/SaveNewContract.tsx +++ b/src/lib/components/modal/contract/SaveNewContract.tsx @@ -13,7 +13,7 @@ import { OffChainForm } from "lib/components/OffChainForm"; import { INSTANTIATED_LIST_NAME } from "lib/data"; import { useHandleContractSave } from "lib/hooks"; import { useContractStore } from "lib/providers/store"; -import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; +import { useContractLcd } from "lib/services/wasm/contract"; import type { BechAddr, BechAddr32, LVPair } from "lib/types"; import { formatSlugName, @@ -88,15 +88,14 @@ export function SaveNewContractModal({ }); }; - const { refetch } = useContractDetailByContractAddress( - contractAddressState as BechAddr32, - (data) => { + const { refetch } = useContractLcd(contractAddressState as BechAddr32, { + onSuccess: (data) => { const contractLocalInfo = getContractLocalInfo(contractAddressState); reset({ contractAddress: contractAddressState, - instantiator: data.instantiator, - label: data.label, - name: contractLocalInfo?.name ?? data.label, + instantiator: data.contract.instantiator, + label: data.contract.label, + name: contractLocalInfo?.name ?? data.contract.label, description: getNameAndDescriptionDefault( contractLocalInfo?.description ), @@ -113,14 +112,14 @@ export function SaveNewContractModal({ message: "Valid Contract Address", }); }, - (err) => { + onError: (err) => { resetForm(false); setStatus({ state: "error", - message: err.message, + message: (err as Error).message, }); - } - ); + }, + }); useEffect(() => { if (contractAddressState.trim().length === 0) { diff --git a/src/lib/components/select-contract/SelectContractInstantiator.tsx b/src/lib/components/select-contract/SelectContractInstantiator.tsx index 62ebe7b2c..93feb0f86 100644 --- a/src/lib/components/select-contract/SelectContractInstantiator.tsx +++ b/src/lib/components/select-contract/SelectContractInstantiator.tsx @@ -13,16 +13,13 @@ import { Text, useDisclosure, } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import type { AxiosError } from "axios"; -import { useState } from "react"; import type { KeyboardEvent } from "react"; +import { useState } from "react"; import { CustomIcon } from "../icon"; import { AmpEvent, track } from "lib/amplitude"; import { - CELATONE_QUERY_KEYS, - useBaseApiRoute, useExampleAddresses, useMobile, useValidateAddress, @@ -30,7 +27,7 @@ import { import { DEFAULT_RPC_ERROR } from "lib/data"; import { useInstantiatedByMe } from "lib/model/contract"; import { useContractStore } from "lib/providers/store"; -import { queryContract } from "lib/services/wasm/contract"; +import { useContractLcd } from "lib/services/wasm/contract"; import type { BechAddr32, RpcQueryError } from "lib/types"; import { AllContractLists } from "./AllContractLists"; @@ -45,11 +42,12 @@ export const SelectContractInstantiator = ({ notSelected, onContractSelect, }: SelectContractInstantiatorProps) => { + const isMobile = useMobile(); const { contract: exampleContractAddress } = useExampleAddresses(); const { isOpen, onOpen, onClose } = useDisclosure(); - const [listSlug, setListSlug] = useState(""); const { validateContractAddress } = useValidateAddress(); + const [listSlug, setListSlug] = useState(""); const [searchContract, setSearchContract] = useState( "" as BechAddr32 ); @@ -57,15 +55,11 @@ export const SelectContractInstantiator = ({ const { getContractLists } = useContractStore(); - const isMobile = useMobile(); - // TODO - Revisit false case const { instantiatedListInfo, isLoading } = useInstantiatedByMe(true); const contractLists = [instantiatedListInfo, ...getContractLists()]; const contractList = contractLists.find((item) => item.slug === listSlug); - const lcdEndpoint = useBaseApiRoute("rest"); - const resetOnClose = () => { setListSlug(""); setSearchContract("" as BechAddr32); @@ -79,23 +73,18 @@ export const SelectContractInstantiator = ({ resetOnClose(); }; - // TODO: Abstract query - const { refetch, isFetching, isRefetching } = useQuery( - [CELATONE_QUERY_KEYS.CONTRACT_INFO, lcdEndpoint, searchContract], - async () => queryContract(lcdEndpoint, searchContract), - { - enabled: false, - retry: false, - cacheTime: 0, - refetchOnReconnect: false, - onSuccess() { - onSelectThenClose(searchContract); - }, - onError(err: AxiosError) { - setInvalid(err.response?.data.message || DEFAULT_RPC_ERROR); - }, - } - ); + const { refetch, isFetching, isRefetching } = useContractLcd(searchContract, { + enabled: false, + retry: false, + cacheTime: 0, + refetchOnReconnect: false, + onSuccess: () => onSelectThenClose(searchContract), + onError: (err) => + setInvalid( + (err as AxiosError).response?.data.message || + DEFAULT_RPC_ERROR + ), + }); const handleListSelect = (slug: string) => { setListSlug(slug); diff --git a/src/lib/pages/admin/index.tsx b/src/lib/pages/admin/index.tsx index e9f828f3b..9f80bfc99 100644 --- a/src/lib/pages/admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -23,7 +23,7 @@ import { TextInput } from "lib/components/forms"; import { UserDocsLink } from "lib/components/UserDocsLink"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useTxBroadcast } from "lib/hooks"; -import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; +import { useContractLcd } from "lib/services/wasm/contract"; import type { BechAddr, BechAddr32 } from "lib/types"; import { MsgType } from "lib/types"; import { composeMsg, getFirstQueryParam } from "lib/utils"; @@ -107,13 +107,12 @@ const UpdateAdmin = () => { /** * @remarks Contract admin validation */ - useContractDetailByContractAddress( - contractAddressParam, - (contractDetail) => { - if (contractDetail.admin !== address) onContractPathChange(); + useContractLcd(contractAddressParam, { + onSuccess: (data) => { + if (data.contract.admin !== address) onContractPathChange(); }, - () => onContractPathChange() - ); + onError: () => onContractPathChange(), + }); useEffect(() => { if (contractAddressParam && validateContractAddress(contractAddressParam)) { diff --git a/src/lib/pages/contract-details/data.ts b/src/lib/pages/contract-details/data.ts index 95250a9ba..3347a5f74 100644 --- a/src/lib/pages/contract-details/data.ts +++ b/src/lib/pages/contract-details/data.ts @@ -1,15 +1,15 @@ import { useCodeStore, useContractStore } from "lib/providers/store"; import { - useContractDataByContractAddress, + useContractData, useMigrationHistoriesByContractAddress, } from "lib/services/wasm/contract"; import type { BechAddr32, ContractMigrationHistory } from "lib/types"; -export const useContractData = (contractAddress: BechAddr32) => { +export const useContractDataWithLocalInfos = (contractAddress: BechAddr32) => { const { getCodeLocalInfo } = useCodeStore(); const { getContractLocalInfo } = useContractStore(); - const result = useContractDataByContractAddress(contractAddress); + const result = useContractData(contractAddress); const codeLocalInfo = result.data?.contract ? getCodeLocalInfo(Number(result.data.contract.codeId)) diff --git a/src/lib/pages/contract-details/index.tsx b/src/lib/pages/contract-details/index.tsx index 1853dfc96..bc4c82b74 100644 --- a/src/lib/pages/contract-details/index.tsx +++ b/src/lib/pages/contract-details/index.tsx @@ -38,7 +38,7 @@ import { ContractStates } from "./components/contract-states"; import { ContractTop } from "./components/ContractTop"; import { InstantiateInfo } from "./components/InstantiateInfo"; import { ContractTables } from "./components/tables"; -import { useContractData } from "./data"; +import { useContractDataWithLocalInfos } from "./data"; import { TabIndex, zContractDetailsQueryParams } from "./types"; const InvalidContract = () => ; @@ -64,7 +64,7 @@ const ContractDetailsBody = observer( contractLocalInfo, data: contractData, isLoading, - } = useContractData(contractAddress); + } = useContractDataWithLocalInfos(contractAddress); const { data: balances, isLoading: isBalancesLoading } = useBalances(contractAddress); diff --git a/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx b/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx index 718dbbb38..feed69be4 100644 --- a/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx +++ b/src/lib/pages/interact-contract/components/query-area/JsonQuery.tsx @@ -1,15 +1,10 @@ import { Box, ButtonGroup, Flex, Spacer, Text } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import type { AxiosError } from "axios"; import dynamic from "next/dynamic"; import { useEffect, useState } from "react"; import { AmpEvent, track, trackActionQuery } from "lib/amplitude"; -import { - CELATONE_QUERY_KEYS, - useBaseApiRoute, - useCurrentChain, -} from "lib/app-provider"; +import { useCurrentChain } from "lib/app-provider"; import { SubmitButton } from "lib/components/button"; import { ContractCmdButton } from "lib/components/ContractCmdButton"; import { CopyButton } from "lib/components/copy"; @@ -18,7 +13,10 @@ import JsonReadOnly from "lib/components/json/JsonReadOnly"; import { LoadingOverlay } from "lib/components/LoadingOverlay"; import { DEFAULT_RPC_ERROR } from "lib/data"; import { useContractStore } from "lib/providers/store"; -import { queryData, useContractQueryMsgs } from "lib/services/wasm/contract"; +import { + useContractQueryLcd, + useContractQueryMsgs, +} from "lib/services/wasm/contract"; import type { BechAddr32, RpcQueryError } from "lib/types"; import { encode, @@ -43,7 +41,6 @@ interface JsonQueryProps { export const JsonQuery = ({ contractAddress, initialMsg }: JsonQueryProps) => { const { data: queryCmds = [], isFetching: isCmdsFetching } = useContractQueryMsgs(contractAddress); - const lcdEndpoint = useBaseApiRoute("rest"); const { addActivity } = useContractStore(); const { address } = useCurrentChain(); @@ -55,37 +52,28 @@ export const JsonQuery = ({ contractAddress, initialMsg }: JsonQueryProps) => { setRes(""); }, [contractAddress, initialMsg]); - // TODO: Abstract query - const { - refetch, - isFetching: queryFetching, - isRefetching: queryRefetching, - } = useQuery( - [CELATONE_QUERY_KEYS.CONTRACT_QUERY, lcdEndpoint, contractAddress, msg], - async () => queryData(lcdEndpoint, contractAddress, msg), - { - enabled: false, - retry: false, - cacheTime: 0, - onSettled() { - setMsg(jsonPrettify(msg)); - }, - onSuccess(data) { - setRes(JSON.stringify(data.data, null, 2)); - addActivity({ - type: "query", - action: Object.keys(JSON.parse(msg))[0] ?? "Unknown", - sender: address, - contractAddress, - msg: encode(msg), - timestamp: getCurrentDate(), - }); - }, - onError(err: AxiosError) { - setRes(err.response?.data.message || DEFAULT_RPC_ERROR); - }, - } - ); + const { refetch, isFetching } = useContractQueryLcd(contractAddress, msg, { + enabled: false, + retry: false, + cacheTime: 0, + onSettled: () => setMsg(jsonPrettify(msg)), + onSuccess: (data) => { + setRes(JSON.stringify(data, null, 2)); + addActivity({ + type: "query", + action: Object.keys(JSON.parse(msg))[0] ?? "Unknown", + sender: address, + contractAddress, + msg: encode(msg), + timestamp: getCurrentDate(), + }); + }, + onError: (err) => + setRes( + (err as AxiosError).response?.data.message || + DEFAULT_RPC_ERROR + ), + }); const handleQuery = () => { trackActionQuery(AmpEvent.ACTION_QUERY, "json-input", true); @@ -151,7 +139,7 @@ export const JsonQuery = ({ contractAddress, initialMsg }: JsonQueryProps) => { diff --git a/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx b/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx index 3a32dc157..dfe6a8576 100644 --- a/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx +++ b/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryComponent.tsx @@ -12,13 +12,11 @@ import { GridItem, Text, } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import type { AxiosError } from "axios"; import dynamic from "next/dynamic"; import { useCallback, useEffect, useState } from "react"; import { AmpEvent, track, trackActionQuery } from "lib/amplitude"; -import { CELATONE_QUERY_KEYS } from "lib/app-provider"; import { CopyButton } from "lib/components/copy"; import { CustomIcon } from "lib/components/icon"; import { @@ -27,7 +25,7 @@ import { OutputMessageTabs, } from "lib/components/json-schema"; import { DEFAULT_RPC_ERROR } from "lib/data"; -import { queryData } from "lib/services/wasm/contract"; +import { useContractQueryLcd } from "lib/services/wasm/contract"; import type { Activity } from "lib/stores/contract"; import type { SchemaInfo } from "lib/stores/schema"; import type { @@ -58,7 +56,6 @@ interface SchemaQueryComponentProps { msgSchema: SchemaInfo; resSchema: SchemaInfo; contractAddress: BechAddr32; - lcdEndpoint: string; walletAddress: Option; initialMsg: JsonDataType; opened: boolean; @@ -69,7 +66,6 @@ export const SchemaQueryComponent = ({ msgSchema, resSchema, contractAddress, - lcdEndpoint, walletAddress, initialMsg, opened, @@ -83,54 +79,42 @@ export const SchemaQueryComponent = ({ const [queryError, setQueryError] = useState(""); const [timestamp, setTimestamp] = useState(); - // TODO: Abstract query - const { - refetch, - isFetching: queryFetching, - isRefetching: queryRefetching, - } = useQuery( - [ - CELATONE_QUERY_KEYS.CONTRACT_QUERY, - lcdEndpoint, - contractAddress, - msgSchema.title, - msg, - ], - async () => queryData(lcdEndpoint, contractAddress, msg), - { - enabled: !msgSchema.inputRequired && opened, - retry: false, - cacheTime: 0, - onSuccess(data) { - const currentDate = getCurrentDate(); - setQueryError(""); - setRes(JSON.stringify(data.data, null, 2)); - setTimestamp(currentDate); - addActivity({ - type: "query", - action: msgSchema.title ?? "Unknown", - sender: walletAddress, - contractAddress, - msg: encode(msg), - timestamp: currentDate, - }); - trackActionQuery( - AmpEvent.ACTION_QUERY, - "schema", - Boolean(msgSchema.inputRequired) - ); - }, - onError(err: AxiosError) { - setQueryError(err.response?.data.message || DEFAULT_RPC_ERROR); - setTimestamp(undefined); - setRes(""); - }, - } - ); + const { refetch, isFetching } = useContractQueryLcd(contractAddress, msg, { + enabled: !msgSchema.inputRequired && opened, + retry: false, + cacheTime: 0, + onSuccess: (data) => { + const currentDate = getCurrentDate(); + setQueryError(""); + setRes(JSON.stringify(data, null, 2)); + setTimestamp(currentDate); + addActivity({ + type: "query", + action: msgSchema.title ?? "Unknown", + sender: walletAddress, + contractAddress, + msg: encode(msg), + timestamp: currentDate, + }); + }, + onError: (err) => { + setQueryError( + (err as AxiosError).response?.data.message || + DEFAULT_RPC_ERROR + ); + setTimestamp(undefined); + setRes(""); + }, + }); const handleQuery = useCallback(() => { + trackActionQuery( + AmpEvent.ACTION_QUERY, + "schema", + Boolean(msgSchema.inputRequired) + ); refetch(); - }, [refetch]); + }, [msgSchema.inputRequired, refetch]); useEffect(() => { if (isNonEmptyJsonData(initialMsg)) { @@ -187,7 +171,7 @@ export const SchemaQueryComponent = ({ size="sm" onClick={handleQuery} isDisabled={jsonValidate(msg) !== null} - isLoading={queryFetching || queryRefetching} + isLoading={isFetching} leftIcon={} ml="auto" > @@ -227,7 +211,7 @@ export const SchemaQueryComponent = ({ msgSchema={msgSchema} resSchema={resSchema} timestamp={timestamp} - isRefetching={queryFetching || queryRefetching} + isLoading={isFetching} /> {!msgSchema.inputRequired ? ( @@ -257,7 +241,7 @@ export const SchemaQueryComponent = ({ track(AmpEvent.USE_JSON_QUERY_AGAIN); }} isDisabled={jsonValidate(msg) !== null} - isLoading={queryFetching || queryRefetching} + isLoading={isFetching} leftIcon={} ml="auto" > diff --git a/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryResponse.tsx b/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryResponse.tsx index 56dfbdff8..8fc878cbc 100644 --- a/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryResponse.tsx +++ b/src/lib/pages/interact-contract/components/query-area/schema-query/SchemaQueryResponse.tsx @@ -13,7 +13,7 @@ interface SchemaQueryResponseProps { msgSchema: SchemaInfo; resSchema: SchemaInfo; timestamp: Option; - isRefetching: boolean; + isLoading: boolean; } const TimestampText = memo(({ timestamp }: { timestamp: Option }) => { @@ -39,9 +39,9 @@ export const SchemaQueryResponse = ({ msgSchema, resSchema, timestamp, - isRefetching, + isLoading, }: SchemaQueryResponseProps) => { - if (isRefetching) + if (isLoading) return ( { const { addActivity } = useContractStore(); const { address } = useCurrentChain(); - const lcdEndpoint = useBaseApiRoute("rest"); const { getSchemaByCodeHash } = useSchemaStore(); const fullSchema = getSchemaByCodeHash(codeHash); @@ -148,7 +147,6 @@ export const SchemaQuery = ({ msgSchema={msg} resSchema={res} contractAddress={contractAddress} - lcdEndpoint={lcdEndpoint} walletAddress={address} initialMsg={resolveInitialMsg(initialMsg, msg)} addActivity={addActivity} diff --git a/src/lib/pages/interact-contract/index.tsx b/src/lib/pages/interact-contract/index.tsx index f10270191..c6076bdfd 100644 --- a/src/lib/pages/interact-contract/index.tsx +++ b/src/lib/pages/interact-contract/index.tsx @@ -12,9 +12,9 @@ import { ContractSelectSection } from "lib/components/ContractSelectSection"; import PageContainer from "lib/components/PageContainer"; import { InvalidState } from "lib/components/state"; import { UserDocsLink } from "lib/components/UserDocsLink"; -import type { ContractDetail } from "lib/services/types"; -import { ContractInteractionTabs } from "lib/types"; +import { useCodeInfoLcd } from "lib/services/wasm/code"; import type { BechAddr32, Coin } from "lib/types"; +import { ContractInteractionTabs } from "lib/types"; import { jsonPrettify, jsonValidate, libDecode } from "lib/utils"; import { @@ -43,7 +43,11 @@ const InteractContractBody = ({ const [initialMsg, setInitialMsg] = useState(""); const [initialFunds, setInitialFunds] = useState([]); const [codeId, setCodeId] = useState(); - const [codeHash, setCodeHash] = useState(); + + // ------------------------------------------// + // ---------------DEPENDENCIES---------------// + // ------------------------------------------// + const { data: code } = useCodeInfoLcd(codeId?.toString() ?? ""); // ------------------------------------------// // ----------------CALLBACKS-----------------// @@ -96,7 +100,6 @@ const InteractContractBody = ({ useEffect(() => { setContractAddress(contract); setCodeId(undefined); - setCodeHash(undefined); if (!msg) { setInitialMsg(""); @@ -127,10 +130,7 @@ const InteractContractBody = ({ mode="all-lists" contractAddress={contract} onContractSelect={onContractSelect} - successCallback={(data: ContractDetail) => { - setCodeHash(data.codeHash); - setCodeId(data.codeId); - }} + successCallback={(data) => setCodeId(data.contract.codeId)} /> @@ -150,7 +150,7 @@ const InteractContractBody = ({ contractAddress={contractAddress} initialMsg={initialMsg} codeId={codeId} - codeHash={codeHash} + codeHash={code?.codeInfo.dataHash} /> } executeContent={ @@ -159,7 +159,7 @@ const InteractContractBody = ({ initialMsg={initialMsg} initialFunds={initialFunds} codeId={codeId} - codeHash={codeHash} + codeHash={code?.codeInfo.dataHash} /> } /> diff --git a/src/lib/pages/migrate/index.tsx b/src/lib/pages/migrate/index.tsx index d76a2e360..5a9dfa06b 100644 --- a/src/lib/pages/migrate/index.tsx +++ b/src/lib/pages/migrate/index.tsx @@ -18,7 +18,7 @@ import { Stepper } from "lib/components/stepper"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useUploadCode } from "lib/hooks"; import { useUploadAccessParams } from "lib/services/proposalService"; -import { useContractDetailByContractAddress } from "lib/services/wasm/contract"; +import { useContractLcd } from "lib/services/wasm/contract"; import type { BechAddr32 } from "lib/types"; import { getFirstQueryParam } from "lib/utils"; @@ -82,21 +82,20 @@ const Migrate = () => { [codeIdParam, firstStep, navigate] ); - useContractDetailByContractAddress( - contractAddress, - (data) => { - if (data.admin === address) { - setValue("admin", data.admin); + useContractLcd(contractAddress, { + onSuccess: (data) => { + if (data.contract.admin === address) { + setValue("admin", data.contract.admin); } else { setValue("admin", defaultValues.admin); setValue("contractAddress", defaultValues.contractAddress); } }, - () => { + onError: () => { setValue("admin", defaultValues.admin); setValue("contractAddress", defaultValues.contractAddress); - } - ); + }, + }); useEffect(() => { setValue("contractAddress", contractAddressParam); diff --git a/src/lib/services/nameService.ts b/src/lib/services/nameService.ts index c8b046c58..58ea344e3 100644 --- a/src/lib/services/nameService.ts +++ b/src/lib/services/nameService.ts @@ -15,7 +15,7 @@ import type { BechAddr, BechAddr32, Option } from "lib/types"; import type { ICNSNamesResponse } from "./ns"; import { queryAddressByICNSName, queryICNSNamesByAddress } from "./ns"; -import { queryContract } from "./wasm/contract"; +import { getContractLcd } from "./wasm/contract/lcd"; export const useICNSNamesByAddress = ( address: Option @@ -74,7 +74,7 @@ export const useAddressByICNSName = ( ); let addressType = getAddressType(icnsAddress); if (addressType === "contract_address") { - const contractData = await queryContract( + const contractData = await getContractLcd( lcdEndpoint, icnsAddress as BechAddr32 ); diff --git a/src/lib/services/searchService.ts b/src/lib/services/searchService.ts index d953ff263..83baea950 100644 --- a/src/lib/services/searchService.ts +++ b/src/lib/services/searchService.ts @@ -1,16 +1,13 @@ -import { useQuery } from "@tanstack/react-query"; import { useEffect, useMemo, useState } from "react"; import { - CELATONE_QUERY_KEYS, - useBaseApiRoute, useCelatoneApp, useCurrentChain, useGetAddressType, useValidateAddress, } from "lib/app-provider"; -import { zBechAddr32, zValidatorAddr } from "lib/types"; import type { BechAddr, Option } from "lib/types"; +import { zBechAddr32, zValidatorAddr } from "lib/types"; import { isHexModuleAddress, isHexWalletAddress, @@ -28,7 +25,7 @@ import { useProposalData } from "./proposalService"; import { useTxData } from "./tx"; import { useValidatorData } from "./validator"; import { useCodeDataByCodeId } from "./wasm/code"; -import { queryContract } from "./wasm/contract"; +import { useContractLcd } from "./wasm/contract"; export type SearchResultType = | "Code ID" @@ -70,23 +67,23 @@ export const useSearchHandler = ( }, }, } = useCelatoneApp(); - const lcdEndpoint = useBaseApiRoute("rest"); const { chain: { bech32_prefix: bech32Prefix }, } = useCurrentChain(); + const getAddressType = useGetAddressType(); - const { isSomeValidAddress, validateValidatorAddress } = useValidateAddress(); + const { + validateContractAddress, + validateValidatorAddress, + isSomeValidAddress, + } = useValidateAddress(); const addressType = getAddressType(debouncedKeyword); // Contract - const { data: contractData, isFetching: contractFetching } = useQuery( - [CELATONE_QUERY_KEYS.CONTRACT_INFO, lcdEndpoint, debouncedKeyword], - async () => { - const contractAddr = zBechAddr32.parse(debouncedKeyword); - return queryContract(lcdEndpoint, contractAddr); - }, + const { data: contractData, isFetching: contractFetching } = useContractLcd( + zBechAddr32.parse(debouncedKeyword), { - enabled: isWasm && Boolean(debouncedKeyword), + enabled: isWasm && validateContractAddress(debouncedKeyword) === null, refetchOnWindowFocus: false, retry: false, } diff --git a/src/lib/services/types/wasm/contract.ts b/src/lib/services/types/wasm/contract.ts index 1e670762b..3a0fe6ff5 100644 --- a/src/lib/services/types/wasm/contract.ts +++ b/src/lib/services/types/wasm/contract.ts @@ -1,6 +1,6 @@ import z from "zod"; -import type { ContractLocalInfo } from "lib/stores/contract"; +import type { ContractInfo, ContractMigrationHistory } from "lib/types"; import { zBechAddr, zBechAddr32, @@ -9,31 +9,19 @@ import { zPublicContractInfo, zUtcDate, } from "lib/types"; -import type { - BechAddr, - ContractInfo, - ContractMigrationHistory, - Option, -} from "lib/types"; -import { parseTxHash, snakeToCamel } from "lib/utils"; - -export interface ContractDetail extends ContractLocalInfo { - codeId: number; - codeHash: string; - admin: Option; -} +import { getDefaultDate, parseTxHash, snakeToCamel } from "lib/utils"; export interface ContractCw2Info { contract: string; version: string; } -export const zContractRest = z.object({ +const zContractRest = z.object({ address: zBechAddr32, contract_info: z.object({ code_id: z.string(), creator: zBechAddr, - admin: zBechAddr, + admin: z.string(), label: z.string(), created: z .object({ @@ -97,19 +85,47 @@ export type Contract = z.infer; export const zContractData = z .object({ + contract: zContract, + contract_rest: zContractRest.nullable(), project_info: zProjectInfo.nullable(), public_info: zPublicContractInfo.nullable(), - contract: zContract.nullable(), - contract_rest: zContractRest.nullable(), }) .transform((value) => ({ - projectInfo: value.project_info, - publicInfo: value.public_info, contract: value.contract, contractRest: value.contract_rest, + projectInfo: value.project_info, + publicInfo: value.public_info, })); export type ContractData = z.infer; +export const zContractLcd = zContractRest.transform< + Pick +>((val) => ({ + contract: { + address: val.address, + admin: val.contract_info.admin.length + ? zBechAddr.parse(val.contract_info.admin) + : null, + codeId: Number(val.contract_info.code_id), + // TODO: make optional - get from code + codeHash: "", + createdHeight: Number(val.contract_info.created?.block_height), + // TODO: make optional + createdTimestamp: getDefaultDate(), + cw2Contract: null, + cw2Version: null, + // TODO: make optional - get from histories + initMsg: "", + initProposalId: null, + initProposalTitle: null, + initTxHash: null, + instantiator: val.contract_info.creator, + label: val.contract_info.label, + }, + contractRest: val, +})); +export type ContractLcd = z.infer; + export const zContractTableCounts = z .object({ tx: z.number().nullish(), diff --git a/src/lib/services/wasm/contract/api.ts b/src/lib/services/wasm/contract/api.ts index b39fb8b58..2ba9f8331 100644 --- a/src/lib/services/wasm/contract/api.ts +++ b/src/lib/services/wasm/contract/api.ts @@ -1,39 +1,18 @@ import axios from "axios"; +import type { + ContractsResponse, + ContractTableCounts, +} from "lib/services/types/"; import { zContractData, zContractQueryMsgs, - zContractRest, zContractsResponse, zContractTableCounts, zMigrationHistoriesResponse, } from "lib/services/types/"; -import type { - ContractsResponse, - ContractTableCounts, -} from "lib/services/types/"; import type { BechAddr, BechAddr32 } from "lib/types"; -import { encode, parseWithError } from "lib/utils"; - -export const queryData = async ( - endpoint: string, - contractAddress: BechAddr32, - msg: string -) => { - const b64 = encode(msg); - const { data } = await axios.get( - `${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${b64}` - ); - return data; -}; - -export const queryContract = async ( - endpoint: string, - contractAddress: BechAddr32 -) => - axios(`${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}`).then( - ({ data }) => parseWithError(zContractRest, data) - ); +import { parseWithError } from "lib/utils"; export const getContracts = async ( endpoint: string, @@ -82,7 +61,7 @@ export const getAdminContractsByAddress = async ( }) .then(({ data }) => parseWithError(zContractsResponse, data)); -export const getContractDataByContractAddress = async ( +export const getContractData = async ( endpoint: string, contractAddress: BechAddr32, isGov: boolean diff --git a/src/lib/services/wasm/contract/gql.ts b/src/lib/services/wasm/contract/gql.ts new file mode 100644 index 000000000..635c6ad5f --- /dev/null +++ b/src/lib/services/wasm/contract/gql.ts @@ -0,0 +1,154 @@ +import type { UseQueryResult } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; +import { useCallback } from "react"; + +import { + CELATONE_QUERY_KEYS, + useCelatoneApp, + useWasmConfig, +} from "lib/app-provider"; +import { + getAdminByContractAddressesQueryDocument, + getContractListByAdmin, + getInstantiatedCountByUserQueryDocument, + getInstantiatedListByUserQueryDocument, +} from "lib/query"; +import type { ContractLocalInfo } from "lib/stores/contract"; +import type { BechAddr, BechAddr20, BechAddr32, Dict, Option } from "lib/types"; + +export const useInstantiatedCountByUserQuery = ( + walletAddr: Option +): UseQueryResult> => { + const { indexerGraphClient } = useCelatoneApp(); + const wasm = useWasmConfig({ shouldRedirect: false }); + + const queryFn = useCallback(async () => { + if (!walletAddr) + throw new Error( + "Wallet address not found (useInstantiatedCountByUserQuery)" + ); + + return indexerGraphClient + .request(getInstantiatedCountByUserQueryDocument, { + walletAddr, + }) + .then(({ contracts_aggregate }) => contracts_aggregate?.aggregate?.count); + }, [indexerGraphClient, walletAddr]); + + return useQuery( + [ + CELATONE_QUERY_KEYS.INSTANTIATED_COUNT_BY_WALLET_ADDRESS, + walletAddr, + indexerGraphClient, + ], + queryFn, + { + keepPreviousData: true, + enabled: wasm.enabled && Boolean(walletAddr), + } + ); +}; + +export const useInstantiatedListByUserQuery = ( + walletAddr: Option +): UseQueryResult => { + const { indexerGraphClient } = useCelatoneApp(); + const queryFn = useCallback(async () => { + if (!walletAddr) + throw new Error( + "Wallet address not found (useInstantiatedListByUserQuery)" + ); + + return indexerGraphClient + .request(getInstantiatedListByUserQueryDocument, { + walletAddr, + }) + .then(({ contracts }) => + contracts.map((contractInst) => ({ + contractAddress: contractInst.address as BechAddr32, + instantiator: contractInst.accountByInitBy?.address as BechAddr, + label: contractInst.label, + })) + ); + }, [indexerGraphClient, walletAddr]); + + return useQuery( + [ + CELATONE_QUERY_KEYS.INSTANTIATED_LIST_BY_WALLET_ADDRESS, + walletAddr, + indexerGraphClient, + ], + queryFn, + { enabled: Boolean(walletAddr), refetchOnWindowFocus: false } + ); +}; + +export const useContractListByAdmin = ( + adminAddress: Option +): UseQueryResult => { + const { indexerGraphClient } = useCelatoneApp(); + + const queryFn = useCallback(async () => { + if (!adminAddress) + throw new Error("Admin address not found (useContractListByAdmin)"); + + return indexerGraphClient + .request(getContractListByAdmin, { + address: adminAddress, + }) + .then(({ contracts }) => + contracts.map((contractAdmin) => ({ + contractAddress: contractAdmin.address as BechAddr32, + instantiator: contractAdmin.accountByInitBy?.address as BechAddr, + label: contractAdmin.label, + })) + ); + }, [adminAddress, indexerGraphClient]); + + return useQuery( + [CELATONE_QUERY_KEYS.CONTRACTS_BY_ADMIN, adminAddress, indexerGraphClient], + queryFn, + { + keepPreviousData: true, + enabled: Boolean(adminAddress), + } + ); +}; + +export const useAdminByContractAddresses = ( + contractAddresses: BechAddr32[] +): UseQueryResult> => { + const { indexerGraphClient } = useCelatoneApp(); + + const queryFn = useCallback( + async () => + indexerGraphClient + .request(getAdminByContractAddressesQueryDocument, { + contractAddresses, + }) + .then(({ contracts }) => + contracts.reduce>( + (prev, contract) => ({ + ...prev, + [contract.address as BechAddr32]: contract.admin + ?.address as BechAddr, + }), + {} + ) + ), + [contractAddresses, indexerGraphClient] + ); + + return useQuery( + [ + CELATONE_QUERY_KEYS.ADMINS_BY_CONTRACTS, + contractAddresses, + indexerGraphClient, + ], + queryFn, + { + keepPreviousData: true, + enabled: contractAddresses.length > 0, + } + ); +}; diff --git a/src/lib/services/wasm/contract/index.ts b/src/lib/services/wasm/contract/index.ts index 8ab3cabb0..fe9be5e91 100644 --- a/src/lib/services/wasm/contract/index.ts +++ b/src/lib/services/wasm/contract/index.ts @@ -1,35 +1,24 @@ -import { useQuery } from "@tanstack/react-query"; import type { UseQueryOptions, UseQueryResult } from "@tanstack/react-query"; -import { useCallback } from "react"; +import { useQuery } from "@tanstack/react-query"; -import { getCodeIdInfoLcd } from "../code"; import { CELATONE_QUERY_KEYS, useBaseApiRoute, - useCelatoneApp, useGovConfig, - useWasmConfig, + useLcdEndpoint, } from "lib/app-provider"; -import { - getAdminByContractAddressesQueryDocument, - getContractByContractAddressQueryDocument, - getContractListByAdmin, - getInstantiatedCountByUserQueryDocument, - getInstantiatedListByUserQueryDocument, -} from "lib/query"; import type { ContractData, - ContractDetail, + ContractLcd, ContractsResponse, ContractTableCounts, MigrationHistoriesResponse, } from "lib/services/types"; -import type { ContractLocalInfo } from "lib/stores/contract"; -import type { BechAddr, BechAddr20, BechAddr32, Dict, Option } from "lib/types"; +import type { BechAddr, BechAddr32, JsonDataType } from "lib/types"; import { getAdminContractsByAddress, - getContractDataByContractAddress, + getContractData, getContractQueryMsgs, getContracts, getContractsByCodeId, @@ -37,53 +26,7 @@ import { getInstantiatedContractsByAddress, getMigrationHistoriesByContractAddress, } from "./api"; - -export const useContractDetailByContractAddress = ( - contractAddress: BechAddr32, - onSuccess?: (data: ContractDetail) => void, - onError?: (err: Error) => void -): UseQueryResult => { - const { indexerGraphClient } = useCelatoneApp(); - const lcdEndpoint = useBaseApiRoute("rest"); - - const queryFn = useCallback(async () => { - return indexerGraphClient - .request(getContractByContractAddressQueryDocument, { contractAddress }) - .then(async ({ contracts_by_pk }) => { - if (!contracts_by_pk) throw Error("Contract not found"); - // TODO: retrieve code hash from gql instead when available - const codeHash = await getCodeIdInfoLcd( - lcdEndpoint, - contracts_by_pk.code_id - ).then((data) => data.codeInfo.dataHash); - return { - contractAddress, - codeId: contracts_by_pk.code_id, - codeHash, - label: contracts_by_pk.label, - instantiator: contracts_by_pk.accountByInitBy?.address as BechAddr, - admin: contracts_by_pk.admin - ? (contracts_by_pk.admin.address as BechAddr) - : undefined, - }; - }); - }, [contractAddress, lcdEndpoint, indexerGraphClient]); - - return useQuery( - [ - CELATONE_QUERY_KEYS.CONTRACT_DETAIL_BY_CONTRACT_ADDRESS, - contractAddress, - indexerGraphClient, - ], - queryFn, - { - refetchOnWindowFocus: false, - enabled: Boolean(contractAddress), - onSuccess, - onError, - } - ); -}; +import { getContractLcd, getContractQueryLcd } from "./lcd"; export const useContracts = ( limit: number, @@ -102,143 +45,6 @@ export const useContracts = ( ); }; -export const useInstantiatedCountByUserQuery = ( - walletAddr: Option -): UseQueryResult> => { - const { indexerGraphClient } = useCelatoneApp(); - const wasm = useWasmConfig({ shouldRedirect: false }); - - const queryFn = useCallback(async () => { - if (!walletAddr) - throw new Error( - "Wallet address not found (useInstantiatedCountByUserQuery)" - ); - - return indexerGraphClient - .request(getInstantiatedCountByUserQueryDocument, { - walletAddr, - }) - .then(({ contracts_aggregate }) => contracts_aggregate?.aggregate?.count); - }, [indexerGraphClient, walletAddr]); - - return useQuery( - [ - CELATONE_QUERY_KEYS.INSTANTIATED_COUNT_BY_WALLET_ADDRESS, - walletAddr, - indexerGraphClient, - ], - queryFn, - { - keepPreviousData: true, - enabled: wasm.enabled && Boolean(walletAddr), - } - ); -}; - -export const useInstantiatedListByUserQuery = ( - walletAddr: Option -): UseQueryResult => { - const { indexerGraphClient } = useCelatoneApp(); - const queryFn = useCallback(async () => { - if (!walletAddr) - throw new Error( - "Wallet address not found (useInstantiatedListByUserQuery)" - ); - - return indexerGraphClient - .request(getInstantiatedListByUserQueryDocument, { - walletAddr, - }) - .then(({ contracts }) => - contracts.map((contractInst) => ({ - contractAddress: contractInst.address as BechAddr32, - instantiator: contractInst.accountByInitBy?.address as BechAddr, - label: contractInst.label, - })) - ); - }, [indexerGraphClient, walletAddr]); - - return useQuery( - [ - CELATONE_QUERY_KEYS.INSTANTIATED_LIST_BY_WALLET_ADDRESS, - walletAddr, - indexerGraphClient, - ], - queryFn, - { enabled: Boolean(walletAddr), refetchOnWindowFocus: false } - ); -}; - -export const useContractListByAdmin = ( - adminAddress: Option -): UseQueryResult => { - const { indexerGraphClient } = useCelatoneApp(); - - const queryFn = useCallback(async () => { - if (!adminAddress) - throw new Error("Admin address not found (useContractListByAdmin)"); - - return indexerGraphClient - .request(getContractListByAdmin, { - address: adminAddress, - }) - .then(({ contracts }) => - contracts.map((contractAdmin) => ({ - contractAddress: contractAdmin.address as BechAddr32, - instantiator: contractAdmin.accountByInitBy?.address as BechAddr, - label: contractAdmin.label, - })) - ); - }, [adminAddress, indexerGraphClient]); - - return useQuery( - [CELATONE_QUERY_KEYS.CONTRACTS_BY_ADMIN, adminAddress, indexerGraphClient], - queryFn, - { - keepPreviousData: true, - enabled: Boolean(adminAddress), - } - ); -}; - -export const useAdminByContractAddresses = ( - contractAddresses: BechAddr32[] -): UseQueryResult> => { - const { indexerGraphClient } = useCelatoneApp(); - - const queryFn = useCallback( - async () => - indexerGraphClient - .request(getAdminByContractAddressesQueryDocument, { - contractAddresses, - }) - .then(({ contracts }) => - contracts.reduce>( - (prev, contract) => ({ - ...prev, - [contract.address as BechAddr32]: contract.admin - ?.address as BechAddr, - }), - {} - ) - ), - [contractAddresses, indexerGraphClient] - ); - - return useQuery( - [ - CELATONE_QUERY_KEYS.ADMINS_BY_CONTRACTS, - contractAddresses, - indexerGraphClient, - ], - queryFn, - { - keepPreviousData: true, - enabled: contractAddresses.length > 0, - } - ); -}; - export const useMigrationHistoriesByContractAddress = ( contractAddress: BechAddr32, offset: number, @@ -302,20 +108,30 @@ export const useAdminContractsByAddress = ( ); }; -export const useContractDataByContractAddress = ( - contractAddress: BechAddr32 -) => { +export const useContractData = (contractAddress: BechAddr32) => { const endpoint = useBaseApiRoute("contracts"); const { enabled: isGov } = useGovConfig({ shouldRedirect: false }); return useQuery( [CELATONE_QUERY_KEYS.CONTRACT_DATA, endpoint, contractAddress, isGov], - async () => - getContractDataByContractAddress(endpoint, contractAddress, isGov), + async () => getContractData(endpoint, contractAddress, isGov), { retry: 1, refetchOnWindowFocus: false } ); }; +export const useContractLcd = ( + contractAddress: BechAddr32, + options?: UseQueryOptions +) => { + const endpoint = useLcdEndpoint(); + + return useQuery( + [CELATONE_QUERY_KEYS.CONTRACT_LCD, endpoint, contractAddress], + async () => getContractLcd(endpoint, contractAddress), + options + ); +}; + export const useContractTableCounts = (contractAddress: BechAddr32) => { const endpoint = useBaseApiRoute("contracts"); const { enabled: isGov } = useGovConfig({ shouldRedirect: false }); @@ -365,3 +181,19 @@ export const useContractsByCodeId = ( } ); }; + +export const useContractQueryLcd = ( + contractAddress: BechAddr32, + msg: string, + options: UseQueryOptions +) => { + const endpoint = useLcdEndpoint(); + + return useQuery( + [CELATONE_QUERY_KEYS.CONTRACT_QUERY_LCD, endpoint, contractAddress, msg], + async () => getContractQueryLcd(endpoint, contractAddress, msg), + options + ); +}; + +export * from "./gql"; diff --git a/src/lib/services/wasm/contract/lcd.ts b/src/lib/services/wasm/contract/lcd.ts new file mode 100644 index 000000000..7e8966e1f --- /dev/null +++ b/src/lib/services/wasm/contract/lcd.ts @@ -0,0 +1,21 @@ +import axios from "axios"; + +import { zContractLcd } from "lib/services/types"; +import type { BechAddr32, JsonDataType } from "lib/types"; +import { encode, parseWithError } from "lib/utils"; + +export const getContractQueryLcd = ( + endpoint: string, + contractAddress: BechAddr32, + msg: string +): JsonDataType => + axios + .get( + `${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${encode(msg)}` + ) + .then(({ data }) => data); + +export const getContractLcd = (endpoint: string, contractAddress: BechAddr32) => + axios(`${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}`).then( + ({ data }) => parseWithError(zContractLcd, data) + ); From 124f16205d17fbca525b61f4ece84587e3a7a549 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Tue, 14 May 2024 16:58:47 +0700 Subject: [PATCH 6/7] fix: schema --- src/lib/components/ContractSelectSection.tsx | 1 + src/lib/pages/interact-contract/index.tsx | 5 ++++- src/lib/services/wasm/contract/index.ts | 12 ++++++++++-- src/lib/services/wasm/contract/lcd.ts | 8 ++++---- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/lib/components/ContractSelectSection.tsx b/src/lib/components/ContractSelectSection.tsx index d3b359540..3978c9bf2 100644 --- a/src/lib/components/ContractSelectSection.tsx +++ b/src/lib/components/ContractSelectSection.tsx @@ -158,6 +158,7 @@ export const ContractSelectSection = observer( }); const { refetch, isFetching } = useContractLcd(contractAddress, { + enabled: !!contractAddress, onSuccess: (data) => { successCallback?.(data); reset({ diff --git a/src/lib/pages/interact-contract/index.tsx b/src/lib/pages/interact-contract/index.tsx index c6076bdfd..90bfe4bff 100644 --- a/src/lib/pages/interact-contract/index.tsx +++ b/src/lib/pages/interact-contract/index.tsx @@ -1,4 +1,5 @@ import { Flex, Heading } from "@chakra-ui/react"; +import { isUndefined } from "lodash"; import { useRouter } from "next/router"; import { useCallback, useEffect, useState } from "react"; @@ -47,7 +48,9 @@ const InteractContractBody = ({ // ------------------------------------------// // ---------------DEPENDENCIES---------------// // ------------------------------------------// - const { data: code } = useCodeInfoLcd(codeId?.toString() ?? ""); + const { data: code } = useCodeInfoLcd(codeId?.toString() ?? "", { + enabled: !isUndefined(codeId), + }); // ------------------------------------------// // ----------------CALLBACKS-----------------// diff --git a/src/lib/services/wasm/contract/index.ts b/src/lib/services/wasm/contract/index.ts index fe9be5e91..c3ded8dd2 100644 --- a/src/lib/services/wasm/contract/index.ts +++ b/src/lib/services/wasm/contract/index.ts @@ -128,7 +128,11 @@ export const useContractLcd = ( return useQuery( [CELATONE_QUERY_KEYS.CONTRACT_LCD, endpoint, contractAddress], async () => getContractLcd(endpoint, contractAddress), - options + { + retry: 1, + refetchOnWindowFocus: false, + ...options, + } ); }; @@ -192,7 +196,11 @@ export const useContractQueryLcd = ( return useQuery( [CELATONE_QUERY_KEYS.CONTRACT_QUERY_LCD, endpoint, contractAddress, msg], async () => getContractQueryLcd(endpoint, contractAddress, msg), - options + { + retry: 1, + refetchOnWindowFocus: false, + ...options, + } ); }; diff --git a/src/lib/services/wasm/contract/lcd.ts b/src/lib/services/wasm/contract/lcd.ts index 7e8966e1f..5f8be4db0 100644 --- a/src/lib/services/wasm/contract/lcd.ts +++ b/src/lib/services/wasm/contract/lcd.ts @@ -11,11 +11,11 @@ export const getContractQueryLcd = ( ): JsonDataType => axios .get( - `${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${encode(msg)}` + `${endpoint}/cosmwasm/wasm/v1/contract/${encodeURI(contractAddress)}/smart/${encode(msg)}` ) .then(({ data }) => data); export const getContractLcd = (endpoint: string, contractAddress: BechAddr32) => - axios(`${endpoint}/cosmwasm/wasm/v1/contract/${contractAddress}`).then( - ({ data }) => parseWithError(zContractLcd, data) - ); + axios( + `${endpoint}/cosmwasm/wasm/v1/contract/${encodeURI(contractAddress)}` + ).then(({ data }) => parseWithError(zContractLcd, data)); From 8e6cebce5c55031117bc9cfd908973c5eadfddc6 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Wed, 15 May 2024 15:29:54 +0700 Subject: [PATCH 7/7] fix: comments --- src/lib/services/types/wasm/contract.ts | 19 ++++------------- src/lib/services/wasm/contract/api.ts | 27 +++++++++++-------------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/src/lib/services/types/wasm/contract.ts b/src/lib/services/types/wasm/contract.ts index 3a0fe6ff5..05caad078 100644 --- a/src/lib/services/types/wasm/contract.ts +++ b/src/lib/services/types/wasm/contract.ts @@ -90,11 +90,9 @@ export const zContractData = z project_info: zProjectInfo.nullable(), public_info: zPublicContractInfo.nullable(), }) - .transform((value) => ({ - contract: value.contract, - contractRest: value.contract_rest, - projectInfo: value.project_info, - publicInfo: value.public_info, + .transform(({ contract_rest, ...rest }) => ({ + ...snakeToCamel(rest), + contractRest: contract_rest, })); export type ContractData = z.infer; @@ -146,16 +144,7 @@ const zMigrationHistoriesResponseItem = z timestamp: zUtcDate, uploader: zBechAddr, }) - .transform((val) => ({ - codeId: val.code_id, - cw2Contract: val.cw2_contract, - cw2Version: val.cw2_version, - height: val.height, - remark: val.remark, - sender: val.sender, - timestamp: val.timestamp, - uploader: val.uploader, - })); + .transform(snakeToCamel); export const zMigrationHistoriesResponse = z.object({ items: z.array(zMigrationHistoriesResponseItem), }); diff --git a/src/lib/services/wasm/contract/api.ts b/src/lib/services/wasm/contract/api.ts index 2ba9f8331..0673552ad 100644 --- a/src/lib/services/wasm/contract/api.ts +++ b/src/lib/services/wasm/contract/api.ts @@ -35,15 +35,12 @@ export const getInstantiatedContractsByAddress = async ( offset: number ) => axios - .get( - `${endpoint}/${encodeURIComponent(address)}/wasm/instantiated-contracts`, - { - params: { - limit, - offset, - }, - } - ) + .get(`${endpoint}/${encodeURI(address)}/wasm/instantiated-contracts`, { + params: { + limit, + offset, + }, + }) .then(({ data }) => parseWithError(zContractsResponse, data)); export const getAdminContractsByAddress = async ( @@ -53,7 +50,7 @@ export const getAdminContractsByAddress = async ( offset: number ) => axios - .get(`${endpoint}/${encodeURIComponent(address)}/wasm/admin-contracts`, { + .get(`${endpoint}/${encodeURI(address)}/wasm/admin-contracts`, { params: { limit, offset, @@ -67,7 +64,7 @@ export const getContractData = async ( isGov: boolean ) => axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/info`, { + .get(`${endpoint}/${encodeURI(contractAddress)}/info`, { params: { is_gov: isGov, }, @@ -80,7 +77,7 @@ export const getContractTableCounts = async ( isGov: boolean ): Promise => axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/table-counts`, { + .get(`${endpoint}/${encodeURI(contractAddress)}/table-counts`, { params: { is_gov: isGov, }, @@ -94,7 +91,7 @@ export const getMigrationHistoriesByContractAddress = async ( offset: number ) => axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/migrations`, { + .get(`${endpoint}/${encodeURI(contractAddress)}/migrations`, { params: { limit, offset, @@ -107,7 +104,7 @@ export const getContractQueryMsgs = async ( contractAddress: BechAddr32 ) => axios - .get(`${endpoint}/${encodeURIComponent(contractAddress)}/query-msgs`) + .get(`${endpoint}/${encodeURI(contractAddress)}/query-msgs`) .then(({ data }) => parseWithError(zContractQueryMsgs, data)); export const getContractsByCodeId = async ( @@ -117,7 +114,7 @@ export const getContractsByCodeId = async ( offset: number ): Promise => axios - .get(`${endpoint}/${codeId}/contracts`, { + .get(`${endpoint}/${encodeURIComponent(codeId)}/contracts`, { params: { limit, offset,