diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2bea634..775c8edc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#933](https://github.com/alleslabs/celatone-frontend/pull/933) Support lite version for code details contract list +- [#929](https://github.com/alleslabs/celatone-frontend/pull/929) Support lite version for code details page - [#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 diff --git a/src/lib/app-provider/env.ts b/src/lib/app-provider/env.ts index ed1d840ed..67bb40b6e 100644 --- a/src/lib/app-provider/env.ts +++ b/src/lib/app-provider/env.ts @@ -10,14 +10,6 @@ export enum CELATONE_QUERY_KEYS { // TABLE ACCOUNT_TABLE_COUNTS = "CELATONE_QUERY_ACCOUNT_TABLE_COUNTS", CONTRACT_TABLE_COUNTS = "CELATONE_QUERY_CONTRACT_TABLE_COUNTS", - // CONTRACT,CODE LCD - CODE_INFO = "CELATONE_QUERY_CODE_INFO", - CODE_INFO_LCD = "CELATONE_QUERY_CODE_INFO_LCD", - CONTRACT_LCD = "CELATONE_QUERY_CONTRACT_LCD", - CONTRACT_QUERY_MSGS = "CELATONE_QUERY_CONTRACT_QUERY_MSGS", - 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 ACCOUNT_DATA = "CELATONE_QUERY_ACCOUNT_DATA", ACCOUNT_TYPE = "CELATONE_QUERY_ACCOUNT_TYPE", @@ -34,9 +26,16 @@ export enum CELATONE_QUERY_KEYS { CODES_BY_WALLET_ADDRESS = "CELATONE_QUERY_CODES_BY_WALLET_ADDRESS", CODES_BY_IDS = "CELATONE_QUERY_CODES_BY_IDS", CODE_DATA = "CELATONE_QUERY_CODE_DATA", - // CONTRACT GQL + CODE_DATA_LCD = "CELATONE_QUERY_CODE_DATA_LCD", + // CONTRACT CONTRACTS = "CELATONE_QUERY_CONTRACTS", CONTRACT_DATA = "CELATONE_QUERY_CONTRACT_DATA", + CONTRACT_LCD = "CELATONE_QUERY_CONTRACT_LCD", + CONTRACT_QUERY_MSGS = "CELATONE_QUERY_CONTRACT_QUERY_MSGS", + CONTRACT_QUERY_LCD = "CELATONE_QUERY_CONTRACT_QUERY_LCD", + CONTRACT_STATES = "CELATONE_QUERY_CONTRACT_STATES", + CONTRACTS_BY_CODE_ID = "CELATONE_QUERY_CONTRACTS_BY_CODE_ID", + CONTRACTS_BY_CODE_ID_LCD = "CELATONE_QUERY_CONTRACTS_BY_CODE_ID_LCD", CONTRACTS_BY_ADMIN = "CELATONE_QUERY_CONTRACT_BY_ADMIN", INSTANTIATED_CONTRACTS_BY_ADDRESS = "CELATONE_QUERY_INSTANTIATED_CONTRACTS_BY_ADDRESS", ADMIN_CONTRACTS_BY_ADDRESS = "CELATONE_QUERY_ADMIN_CONTRACTS_BY_ADDRESS", diff --git a/src/lib/components/AlertPaginationLcd.tsx b/src/lib/components/AlertPaginationLcd.tsx new file mode 100644 index 000000000..548926c52 --- /dev/null +++ b/src/lib/components/AlertPaginationLcd.tsx @@ -0,0 +1,9 @@ +import { Alert, AlertDescription } from "@chakra-ui/react"; + +export const AlertPaginationLcd = () => ( + + + Error fetching data from LCD. Please try again later. + + +); diff --git a/src/lib/components/LabelText.tsx b/src/lib/components/LabelText.tsx index 4817f71cd..84624790f 100644 --- a/src/lib/components/LabelText.tsx +++ b/src/lib/components/LabelText.tsx @@ -3,7 +3,7 @@ import { Flex, Text } from "@chakra-ui/react"; import { TooltipInfo } from "./Tooltip"; -interface LabelTextProps extends FlexProps { +export interface LabelTextProps extends FlexProps { label: string; labelWeight?: number; labelColor?: string; diff --git a/src/lib/components/modal/code/SaveNewCode.tsx b/src/lib/components/modal/code/SaveNewCode.tsx index c9ae908f8..4f7b8e121 100644 --- a/src/lib/components/modal/code/SaveNewCode.tsx +++ b/src/lib/components/modal/code/SaveNewCode.tsx @@ -10,9 +10,13 @@ import { NumberInput, TextInput } from "lib/components/forms"; import { CustomIcon } from "lib/components/icon"; import { useGetMaxLengthError } from "lib/hooks"; import { useCodeStore } from "lib/providers/store"; -import { useCodeInfoLcd } from "lib/services/wasm/code"; +import { useCodeLcd } from "lib/services/wasm/code"; import type { BechAddr } from "lib/types"; -import { getNameAndDescriptionDefault, getPermissionHelper } from "lib/utils"; +import { + getNameAndDescriptionDefault, + getPermissionHelper, + isId, +} from "lib/utils"; interface SaveNewCodeModalProps { buttonProps: ButtonProps; @@ -55,25 +59,25 @@ export function SaveNewCodeModal({ buttonProps }: SaveNewCodeModalProps) { const { isCodeIdSaved, saveNewCode, updateCodeInfo, getCodeLocalInfo } = useCodeStore(); - const { refetch, isFetching, isRefetching } = useCodeInfoLcd(codeId, { + const { refetch, isFetching, isRefetching } = useCodeLcd(Number(codeId), { enabled: false, retry: false, cacheTime: 0, - onSuccess(data) { + onSuccess: (data) => { const { message, messageColor } = getPermissionHelper( address, - data.codeInfo.instantiatePermission.permission, - data.codeInfo.instantiatePermission.addresses + data.instantiatePermission, + data.permissionAddresses ); setCodeIdStatus({ state: "success", - message: `${message} (${data.codeInfo.instantiatePermission.permission})`, + message: `${message} (${data.instantiatePermission})`, messageColor, }); - setUploader(data.codeInfo.creator); + setUploader(data.uploader); setUploaderStatus({ state: "success" }); }, - onError() { + onError: () => { setCodeIdStatus({ state: "error", message: "Invalid Code ID" }); setUploader("Not Found"); setUploaderStatus({ state: "error" }); @@ -132,7 +136,8 @@ export function SaveNewCodeModal({ buttonProps }: SaveNewCodeModalProps) { }); } else { const timer = setTimeout(() => { - refetch(); + if (isId(codeId)) refetch(); + else setCodeIdStatus({ state: "error", message: "Invalid Code ID" }); }, 500); return () => clearTimeout(timer); diff --git a/src/lib/components/select-code/CodeSelect.tsx b/src/lib/components/select-code/CodeSelect.tsx index 4c9b9ae98..55267502a 100644 --- a/src/lib/components/select-code/CodeSelect.tsx +++ b/src/lib/components/select-code/CodeSelect.tsx @@ -6,8 +6,8 @@ import { PermissionChip } from "../PermissionChip"; import type { FormStatus } from "lib/components/forms"; import { UploadIcon } from "lib/components/icon"; import { useCodeStore } from "lib/providers/store"; -import type { CodeInfoResponseLcd } from "lib/services/types"; -import { useCodeInfoLcd } from "lib/services/wasm/code"; +import type { Code } from "lib/services/types"; +import { useCodeLcd } from "lib/services/wasm/code"; import { AccessConfigPermission } from "lib/types"; import { isId } from "lib/utils"; @@ -15,7 +15,7 @@ import { CodeSelectDrawerButton } from "./CodeSelectDrawerButton"; interface CodeSelectProps extends Omit { onCodeSelect: (code: string) => void; - setCodeHash: (data: CodeInfoResponseLcd) => void; + setCodeHash: (data: Code) => void; codeId: string; status: FormStatus; } @@ -29,9 +29,9 @@ export const CodeSelect = ({ }: CodeSelectProps) => { const { getCodeLocalInfo } = useCodeStore(); const name = getCodeLocalInfo(Number(codeId))?.name; - const { data: codeInfo } = useCodeInfoLcd(codeId, { - onSuccess: setCodeHash, + const { data } = useCodeLcd(Number(codeId), { enabled: isId(codeId), + onSuccess: setCodeHash, }); const isError = status.state === "error"; @@ -66,12 +66,9 @@ export const CodeSelect = ({ diff --git a/src/lib/components/select-code/CodeSelectSection.tsx b/src/lib/components/select-code/CodeSelectSection.tsx index 62059459e..7704cf137 100644 --- a/src/lib/components/select-code/CodeSelectSection.tsx +++ b/src/lib/components/select-code/CodeSelectSection.tsx @@ -5,7 +5,7 @@ import type { Control, FieldPath, FieldValues } from "react-hook-form"; import { AmpEvent, track } from "lib/amplitude"; import { ControllerInput } from "lib/components/forms"; import type { FormStatus } from "lib/components/forms"; -import type { CodeInfoResponseLcd } from "lib/services/types"; +import type { Code } from "lib/services/types"; import type { Option } from "lib/types"; import { CodeSelect } from "./CodeSelect"; @@ -16,7 +16,7 @@ interface CodeSelectSectionProps { control: Control; error: Option; onCodeSelect: (codeId: string) => void; - setCodeHash: (data: CodeInfoResponseLcd) => void; + setCodeHash: (data: Code) => void; status: FormStatus; } diff --git a/src/lib/components/table/contracts/ContractNameCell.tsx b/src/lib/components/table/contracts/ContractNameCell.tsx index 59476482d..aca12459f 100644 --- a/src/lib/components/table/contracts/ContractNameCell.tsx +++ b/src/lib/components/table/contracts/ContractNameCell.tsx @@ -25,7 +25,11 @@ export const ContractNameCell = ({ return ( 0 + ? contractLocalInfo.label + : "Untitled" + } maxLength={constants.maxContractNameLength} tooltip={contractLocalInfo.description} isReadOnly={isReadOnly} diff --git a/src/lib/components/table/contracts/ContractsTable.tsx b/src/lib/components/table/contracts/ContractsTable.tsx index 7379b20fc..d0f46ff12 100644 --- a/src/lib/components/table/contracts/ContractsTable.tsx +++ b/src/lib/components/table/contracts/ContractsTable.tsx @@ -13,9 +13,10 @@ interface ContractsTableProps { isLoading: boolean; emptyState: JSX.Element; onRowSelect: (contract: BechAddr32) => void; + showTag?: boolean; + showLastUpdate?: boolean; isReadOnly?: boolean; withCTA?: CTAInfo; - withoutTag?: boolean; } export const ContractsTable = ({ @@ -23,9 +24,10 @@ export const ContractsTable = ({ isLoading, emptyState, onRowSelect, + showTag = true, + showLastUpdate = true, isReadOnly = false, withCTA, - withoutTag, }: ContractsTableProps) => { const isMobile = useMobile(); @@ -34,13 +36,11 @@ export const ContractsTable = ({ let templateColumns: string; if (isReadOnly) - templateColumns = - "minmax(160px, 300px) minmax(300px, 3fr) minmax(200px, 2fr) 1fr"; - else if (withoutTag) - templateColumns = "160px minmax(300px, 3fr) 250px 300px 80px"; + templateColumns = `minmax(160px, 300px) minmax(300px, 3fr) minmax(200px, 2fr)${showLastUpdate ? " 1fr" : ""}`; + else if (!showTag) + templateColumns = `160px minmax(300px, 3fr)${showLastUpdate ? " 250px 300px" : ""} 80px`; else - templateColumns = - "160px minmax(300px, 3fr) minmax(200px, 2fr) 150px 260px 80px"; + templateColumns = `160px minmax(300px, 3fr) minmax(200px, 2fr)${showLastUpdate ? " 150px 260px" : ""} 80px`; return isMobile ? ( @@ -55,6 +55,7 @@ export const ContractsTable = ({ } contractInfo={contractInfo} onRowSelect={onRowSelect} + showLastUpdate={showLastUpdate} /> ))} @@ -62,9 +63,10 @@ export const ContractsTable = ({ {contracts.map((contractInfo) => ( ))} diff --git a/src/lib/components/table/contracts/ContractsTableHeader.tsx b/src/lib/components/table/contracts/ContractsTableHeader.tsx index f1e98cf38..d3b6fa074 100644 --- a/src/lib/components/table/contracts/ContractsTableHeader.tsx +++ b/src/lib/components/table/contracts/ContractsTableHeader.tsx @@ -7,23 +7,29 @@ import type { CTAInfo } from "./ContractsTableRowCTA"; export const ContractsTableHeader = ({ templateColumns, + showTag, + showLastUpdate, isReadOnly, withCTA, - withoutTag, }: { templateColumns: GridProps["templateColumns"]; + showTag: boolean; + showLastUpdate: boolean; isReadOnly: boolean; withCTA?: CTAInfo; - withoutTag?: boolean; }) => ( Contract Address Contract Name - {!withoutTag && Tags} - Instantiator + {showTag && Tags} + {showLastUpdate && Instantiator} {!isReadOnly && ( <> - {withCTA ? : Timestamp} + {showLastUpdate && ( + <> + {withCTA ? : Timestamp} + + )} )} diff --git a/src/lib/components/table/contracts/ContractsTableMobileCard.tsx b/src/lib/components/table/contracts/ContractsTableMobileCard.tsx index 217b204f3..d9f05f715 100644 --- a/src/lib/components/table/contracts/ContractsTableMobileCard.tsx +++ b/src/lib/components/table/contracts/ContractsTableMobileCard.tsx @@ -17,6 +17,7 @@ import { ContractInstantiatorCell } from "./ContractInstantiatorCell"; interface ContractsTableMobileCardProps { contractInfo: ContractInfo; onRowSelect: (contract: BechAddr32) => void; + showLastUpdate: boolean; } const InstantiatorRemark = ({ @@ -44,6 +45,7 @@ const InstantiatorRemark = ({ export const ContractsTableMobileCard = ({ contractInfo, onRowSelect, + showLastUpdate, }: ContractsTableMobileCardProps) => ( onRowSelect(contractInfo.contractAddress)} @@ -57,26 +59,28 @@ export const ContractsTableMobileCard = ({ } middleContent={ - -
- - - {contractInfo.name ?? contractInfo.label} - -
-
- - -
-
+ showLastUpdate && ( + +
+ + + {contractInfo.name ?? contractInfo.label} + +
+
+ + +
+
+ ) } bottomContent={ contractInfo.latestUpdated ? ( diff --git a/src/lib/components/table/contracts/ContractsTableRow.tsx b/src/lib/components/table/contracts/ContractsTableRow.tsx index 234bfd070..45a129547 100644 --- a/src/lib/components/table/contracts/ContractsTableRow.tsx +++ b/src/lib/components/table/contracts/ContractsTableRow.tsx @@ -14,18 +14,20 @@ interface ContractsTableRowProps { contractInfo: ContractInfo; templateColumns: string; onRowSelect: (contract: BechAddr32) => void; + showTag: boolean; + showLastUpdate: boolean; isReadOnly: boolean; withCTA?: CTAInfo; - withoutTag?: boolean; } export const ContractsTableRow = ({ contractInfo, templateColumns, onRowSelect, + showTag, + showLastUpdate, isReadOnly, withCTA, - withoutTag, }: ContractsTableRowProps) => ( - {!withoutTag && ( + {showTag && ( )} - - - + + {showLastUpdate && ( + + + + )} {!isReadOnly && ( - + )} ); diff --git a/src/lib/components/table/contracts/ContractsTableRowCTA.tsx b/src/lib/components/table/contracts/ContractsTableRowCTA.tsx index d8abcc3ab..ec74600a6 100644 --- a/src/lib/components/table/contracts/ContractsTableRowCTA.tsx +++ b/src/lib/components/table/contracts/ContractsTableRowCTA.tsx @@ -13,11 +13,7 @@ import { } from "@chakra-ui/react"; import { TableRow } from "../tableComponents"; -import { - useCurrentChain, - useInternalNavigate, - useMobile, -} from "lib/app-provider"; +import { useCurrentChain, useInternalNavigate } from "lib/app-provider"; import { AppLink } from "lib/components/AppLink"; import { CustomIcon } from "lib/components/icon"; import { @@ -27,8 +23,8 @@ import { RemoveContractModal, SaveContractDetailsModal, } from "lib/components/modal"; -import { ContractInteractionTabs } from "lib/types"; import type { ContractInfo, LVPair, Option } from "lib/types"; +import { ContractInteractionTabs } from "lib/types"; import { dateFromNow, formatUTC } from "lib/utils"; const StyledIconButton = chakra(IconButton, { @@ -59,7 +55,6 @@ export const ContractsTableRowCTA = ({ const navigate = useInternalNavigate(); const isAdmin = !!address && address === contractInfo.admin; - const isMobile = useMobile(); return withCTA ? ( <> @@ -201,35 +196,33 @@ export const ContractsTableRowCTA = ({ )} - {!isMobile && ( - - e.stopPropagation()}> - {contractInfo.lists ? ( - } - variant="ghost-primary" - /> - } - /> - ) : ( - } - variant="ghost-gray" - /> - } - /> - )} - - - )} + + e.stopPropagation()}> + {contractInfo.lists ? ( + } + variant="ghost-primary" + /> + } + /> + ) : ( + } + variant="ghost-gray" + /> + } + /> + )} + + ); }; diff --git a/src/lib/pages/code-details/components/code-info/CodeInfoLabelText.tsx b/src/lib/pages/code-details/components/code-info/CodeInfoLabelText.tsx new file mode 100644 index 000000000..69d02871f --- /dev/null +++ b/src/lib/pages/code-details/components/code-info/CodeInfoLabelText.tsx @@ -0,0 +1,14 @@ +import { useTierConfig } from "lib/app-provider"; +import type { LabelTextProps } from "lib/components/LabelText"; +import { LabelText } from "lib/components/LabelText"; + +export const CodeInfoLabelText = (props: LabelTextProps) => { + const isFullTier = useTierConfig() === "full"; + return ( + + ); +}; diff --git a/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx b/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx index 5f6739537..b39e21f09 100644 --- a/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx +++ b/src/lib/pages/code-details/components/code-info/CodeInfoSection.tsx @@ -2,7 +2,6 @@ import { Box, Button, Flex, - Grid, Heading, Text, useDisclosure, @@ -10,17 +9,18 @@ import { import { useCallback } from "react"; import { AmpEvent, track } from "lib/amplitude"; -import { useGetAddressType, useMobile } from "lib/app-provider"; +import { useGetAddressType, useMobile, useTierConfig } from "lib/app-provider"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { CustomIcon } from "lib/components/icon"; import { JsonSchemaModal } from "lib/components/json-schema"; -import { LabelText } from "lib/components/LabelText"; import { PermissionChip } from "lib/components/PermissionChip"; import { ViewPermissionAddresses } from "lib/components/ViewPermissionAddresses"; import type { Code } from "lib/services/types"; import type { Option } from "lib/types"; import { dateFromNow, formatUTC, getAddressTypeText } from "lib/utils"; +import { CodeInfoLabelText } from "./CodeInfoLabelText"; + interface CodeInfoSectionProps { code: Code; chainId: string; @@ -33,16 +33,16 @@ const getMethodSpecificRender = ( transaction: Option ): { methodRender: JSX.Element; storedBlockRender: JSX.Element } => { if (proposal) { - const { height, created, proposalId } = proposal; + const { id, height, created } = proposal; return { methodRender: ( - + - + ), storedBlockRender: height && created ? ( @@ -69,9 +69,9 @@ const getMethodSpecificRender = ( const { hash, height, created } = transaction; return { methodRender: ( - + - + ), storedBlockRender: height && created ? ( @@ -100,7 +100,7 @@ const getMethodSpecificRender = ( * @todo Add genesis conditioning when the view table is available */ return { - methodRender: N/A, + methodRender: N/A, storedBlockRender: N/A, }; }; @@ -111,6 +111,8 @@ export const CodeInfoSection = ({ attached, toJsonSchemaTab, }: CodeInfoSectionProps) => { + const isFullTier = useTierConfig() === "full"; + const { isOpen, onClose, onOpen } = useDisclosure(); const getAddressType = useGetAddressType(); const { @@ -145,17 +147,14 @@ export const CodeInfoSection = ({ Code Info - - {chainId} - - + {chainId} + + - - {methodRender} - + + {isFullTier && methodRender} + - - - - {storedBlockRender} - - + + {isFullTier && ( + + + {storedBlockRender} + + + )} {!isMobile && ( - +
-
+ )} -
+ ); }; diff --git a/src/lib/pages/code-details/components/code-info/CodeTopInfo.tsx b/src/lib/pages/code-details/components/code-info/CodeTopInfo.tsx index f12421b47..a7ccc42dd 100644 --- a/src/lib/pages/code-details/components/code-info/CodeTopInfo.tsx +++ b/src/lib/pages/code-details/components/code-info/CodeTopInfo.tsx @@ -1,7 +1,7 @@ import { Flex, Heading, Image, Text } from "@chakra-ui/react"; import { CTASection } from "../CTASection"; -import { useMobile } from "lib/app-provider"; +import { useMobile, useTierConfig } from "lib/app-provider"; import { Breadcrumb } from "lib/components/Breadcrumb"; import { CopyLink } from "lib/components/CopyLink"; import { CustomIcon } from "lib/components/icon"; @@ -26,6 +26,8 @@ export const CodeTopInfo = ({ projectInfo, publicInfo, }: CodeTopInfoProps) => { + const isFullTier = useTierConfig() === "full"; + const { getCodeLocalInfo } = useCodeStore(); const localCodeInfo = getCodeLocalInfo(codeId); @@ -109,21 +111,23 @@ export const CodeTopInfo = ({ type="code_hash" /> - - - CW2 Info: - - - {cw2Info ?? "N/A"} - - + + CW2 Info: + + + {cw2Info ?? "N/A"} + + + )} {publicInfo && } diff --git a/src/lib/pages/code-details/components/code-info/index.ts b/src/lib/pages/code-details/components/code-info/index.ts index 96efbbba3..91355801b 100644 --- a/src/lib/pages/code-details/components/code-info/index.ts +++ b/src/lib/pages/code-details/components/code-info/index.ts @@ -1,2 +1,3 @@ -export * from "./table/CodeContractsTable"; +export * from "./table/CodeContractsTableFull"; +export * from "./table/CodeContractsTableLite"; export * from "./CodeInfoSection"; diff --git a/src/lib/pages/code-details/components/code-info/table/CodeContractsTable.tsx b/src/lib/pages/code-details/components/code-info/table/CodeContractsTableFull.tsx similarity index 93% rename from src/lib/pages/code-details/components/code-info/table/CodeContractsTable.tsx rename to src/lib/pages/code-details/components/code-info/table/CodeContractsTableFull.tsx index 431b0825e..c075de875 100644 --- a/src/lib/pages/code-details/components/code-info/table/CodeContractsTable.tsx +++ b/src/lib/pages/code-details/components/code-info/table/CodeContractsTableFull.tsx @@ -9,12 +9,12 @@ import type { BechAddr32 } from "lib/types"; import { NoContracts } from "./NoContracts"; -interface CodeContractsTableProps { +interface CodeContractsTableFullProps { codeId: number; } -export const CodeContractsTable = observer( - ({ codeId }: CodeContractsTableProps) => { +export const CodeContractsTableFull = observer( + ({ codeId }: CodeContractsTableFullProps) => { const navigate = useInternalNavigate(); const onRowSelect = (contract: BechAddr32) => navigate({ diff --git a/src/lib/pages/code-details/components/code-info/table/CodeContractsTableLite.tsx b/src/lib/pages/code-details/components/code-info/table/CodeContractsTableLite.tsx new file mode 100644 index 000000000..001131742 --- /dev/null +++ b/src/lib/pages/code-details/components/code-info/table/CodeContractsTableLite.tsx @@ -0,0 +1,55 @@ +import { observer } from "mobx-react-lite"; + +import { useInternalNavigate } from "lib/app-provider"; +import { AlertPaginationLcd } from "lib/components/AlertPaginationLcd"; +import { LoadNext } from "lib/components/LoadNext"; +import { ContractsTable, TableTitle } from "lib/components/table"; +import { useCodeContractsLcd } from "lib/pages/code-details/data"; +import type { BechAddr32 } from "lib/types"; + +import { NoContracts } from "./NoContracts"; + +interface CodeContractsTableLiteProps { + codeId: number; +} + +export const CodeContractsTableLite = observer( + ({ codeId }: CodeContractsTableLiteProps) => { + const navigate = useInternalNavigate(); + const { + data, + error, + fetchNextPage, + hasNextPage, + isLoading, + isFetchingNextPage, + } = useCodeContractsLcd(codeId); + + const onRowSelect = (contract: BechAddr32) => + navigate({ + pathname: "/contracts/[contract]", + query: { contract }, + }); + + return ( + <> + {data && !!error && } + + } + onRowSelect={onRowSelect} + showLastUpdate={false} + /> + {hasNextPage && ( + + )} + + ); + } +); diff --git a/src/lib/pages/code-details/data.ts b/src/lib/pages/code-details/data.ts index fed9022aa..35428717d 100644 --- a/src/lib/pages/code-details/data.ts +++ b/src/lib/pages/code-details/data.ts @@ -2,9 +2,30 @@ import type { UseQueryOptions } from "@tanstack/react-query"; import { useContractStore } from "lib/providers/store"; import type { ContractsResponse } from "lib/services/types"; -import { useContractsByCodeId } from "lib/services/wasm/contract"; +import { useCodeLcd } from "lib/services/wasm/code"; +import { + useContractsByCodeId, + useContractsByCodeIdLcd, +} from "lib/services/wasm/contract"; import type { ContractInfo, Option } from "lib/types"; +export const useCodeDataLcd = (codeId: number, enabled: boolean) => { + const { data, isLoading } = useCodeLcd(codeId, { + enabled, + }); + + return { + data: data + ? { + info: data, + projectInfo: null, + publicInfo: null, + } + : undefined, + isLoading, + }; +}; + export const useCodeContracts = ( codeId: number, limit: number, @@ -34,3 +55,18 @@ export const useCodeContracts = ( isLoading, }; }; + +export const useCodeContractsLcd = (codeId: number) => { + const { getContractLocalInfo } = useContractStore(); + const { data, ...rest } = useContractsByCodeIdLcd(codeId); + + return { + data: data?.pages.flatMap((page) => + page.contracts.map((contract) => ({ + ...contract, + ...getContractLocalInfo(contract.contractAddress), + })) + ), + ...rest, + }; +}; diff --git a/src/lib/pages/code-details/index.tsx b/src/lib/pages/code-details/index.tsx index 0175316f2..4e765f96c 100644 --- a/src/lib/pages/code-details/index.tsx +++ b/src/lib/pages/code-details/index.tsx @@ -8,6 +8,7 @@ import { useCelatoneApp, useInternalNavigate, useMobile, + useTierConfig, useWasmConfig, } from "lib/app-provider"; import { CustomTab } from "lib/components/CustomTab"; @@ -16,11 +17,16 @@ import PageContainer from "lib/components/PageContainer"; import { ErrorFetching, InvalidState } from "lib/components/state"; import { UserDocsLink } from "lib/components/UserDocsLink"; import { useSchemaStore } from "lib/providers/store"; -import { useCodeDataByCodeId } from "lib/services/wasm/code"; +import { useCodeData } from "lib/services/wasm/code"; -import { CodeContractsTable, CodeInfoSection } from "./components/code-info"; +import { + CodeContractsTableFull, + CodeContractsTableLite, + CodeInfoSection, +} from "./components/code-info"; import { CodeTopInfo } from "./components/code-info/CodeTopInfo"; import { CodeSchemaSection } from "./components/json-schema/CodeSchemaSection"; +import { useCodeDataLcd } from "./data"; import { TabIndex, zCodeDetailsQueryParams } from "./types"; const codeTabId = "codeDetailsTab"; @@ -34,18 +40,15 @@ const InvalidCode = () => ; const CodeDetailsBody = observer(({ codeId, tab }: CodeDetailsBodyProps) => { const isMobile = useMobile(); + const isFullTier = useTierConfig() === "full"; - const router = useRouter(); const navigate = useInternalNavigate(); const { getSchemaByCodeHash } = useSchemaStore(); const { currentChainId } = useCelatoneApp(); - const { data, isLoading } = useCodeDataByCodeId(codeId); - - useEffect(() => { - if (router.isReady) track(AmpEvent.TO_CODE_DETAILS, { tab }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [router.isReady]); + const resApi = useCodeData(codeId, isFullTier); + const resLcd = useCodeDataLcd(codeId, !isFullTier); + const { data, isLoading } = isFullTier ? resApi : resLcd; const handleTabChange = useCallback( (nextTab: TabIndex) => () => { @@ -83,7 +86,11 @@ const CodeDetailsBody = observer(({ codeId, tab }: CodeDetailsBodyProps) => { index={Object.values(TabIndex).indexOf(tab)} isLazy lazyBehavior="keepMounted" - my={{ base: 0, md: 8 }} + my={8} + borderTop={{ + base: "1px solid var(--chakra-colors-gray-700)", + md: "none", + }} > {!isMobile && ( { attached={!!jsonSchema} toJsonSchemaTab={handleTabChange(TabIndex.JsonSchema)} /> - + {isFullTier ? ( + + ) : ( + + )} { const router = useRouter(); const validated = zCodeDetailsQueryParams.safeParse(router.query); + useEffect(() => { + if (router.isReady && validated.success) + track(AmpEvent.TO_CODE_DETAILS, { tab: validated.data.tab }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router.isReady]); + return ( {validated.success ? ( diff --git a/src/lib/pages/codes/components/RecentCodesTableLite.tsx b/src/lib/pages/codes/components/RecentCodesTableLite.tsx index 1507926a9..6125324fb 100644 --- a/src/lib/pages/codes/components/RecentCodesTableLite.tsx +++ b/src/lib/pages/codes/components/RecentCodesTableLite.tsx @@ -1,15 +1,14 @@ -import { Alert, AlertDescription } from "@chakra-ui/react"; import { observer } from "mobx-react-lite"; import { useRecentCodesLcd } from "../data"; import { useInternalNavigate } from "lib/app-provider"; +import { AlertPaginationLcd } from "lib/components/AlertPaginationLcd"; import { LoadNext } from "lib/components/LoadNext"; import { EmptyState } from "lib/components/state"; import { CodesTable } from "lib/components/table"; export const RecentCodesTableLite = observer(() => { const navigate = useInternalNavigate(); - const { data, error, @@ -27,13 +26,7 @@ export const RecentCodesTableLite = observer(() => { return ( <> - {data && !!error && ( - - - Error fetching data from LCD. Please refresh to try again. - - - )} + {data && !!error && } { ) } onRowSelect={onRowSelect} - withoutTag + showTag={false} /> {data && data.total > 10 && ( { }, }); - const { refetch } = useCodeInfoLcd(codeId, { + const { refetch } = useCodeLcd(Number(codeId), { enabled: false, retry: false, cacheTime: 0, - onSuccess(data) { - const permission = data.codeInfo.instantiatePermission; - setValue("codeHash", data.codeInfo.dataHash.toLowerCase()); + onSuccess: (data) => { + setValue("codeHash", data.hash.toLowerCase()); if ( - resolvePermission(address, permission.permission, permission.addresses) + resolvePermission( + address, + data.instantiatePermission, + data.permissionAddresses + ) ) setStatus({ state: "success" }); else { @@ -212,7 +215,7 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { }); } }, - onError() { + onError: () => { setStatus({ state: "error", message: "This code ID does not exist" }); setSimulateError(""); }, @@ -323,7 +326,8 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { if (codeId.length) { setStatus({ state: "loading" }); const timer = setTimeout(() => { - refetch(); + if (isId(codeId)) refetch(); + else setStatus({ state: "error", message: "Invalid Code ID" }); }, 500); return () => clearTimeout(timer); } @@ -412,9 +416,9 @@ const Instantiate = ({ onComplete }: InstantiatePageProps) => { setValue("codeId", code); resetMsgInputSchema(); }} - setCodeHash={(data: CodeInfoResponseLcd) => { - setValue("codeHash", data.codeInfo.dataHash.toLowerCase()); - }} + setCodeHash={(data: Code) => + setValue("codeHash", data.hash.toLowerCase()) + } codeId={codeId} />
diff --git a/src/lib/pages/interact-contract/index.tsx b/src/lib/pages/interact-contract/index.tsx index 90bfe4bff..8a2b9019b 100644 --- a/src/lib/pages/interact-contract/index.tsx +++ b/src/lib/pages/interact-contract/index.tsx @@ -13,7 +13,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 { useCodeInfoLcd } from "lib/services/wasm/code"; +import { useCodeLcd } from "lib/services/wasm/code"; import type { BechAddr32, Coin } from "lib/types"; import { ContractInteractionTabs } from "lib/types"; import { jsonPrettify, jsonValidate, libDecode } from "lib/utils"; @@ -48,7 +48,7 @@ const InteractContractBody = ({ // ------------------------------------------// // ---------------DEPENDENCIES---------------// // ------------------------------------------// - const { data: code } = useCodeInfoLcd(codeId?.toString() ?? "", { + const { data: code } = useCodeLcd(codeId ?? 0, { enabled: !isUndefined(codeId), }); @@ -153,7 +153,7 @@ const InteractContractBody = ({ contractAddress={contractAddress} initialMsg={initialMsg} codeId={codeId} - codeHash={code?.codeInfo.dataHash} + codeHash={code?.hash} /> } executeContent={ @@ -162,7 +162,7 @@ const InteractContractBody = ({ initialMsg={initialMsg} initialFunds={initialFunds} codeId={codeId} - codeHash={code?.codeInfo.dataHash} + codeHash={code?.hash} /> } /> diff --git a/src/lib/pages/migrate/components/MigrateContract.tsx b/src/lib/pages/migrate/components/MigrateContract.tsx index 023c33efd..fce3a1530 100644 --- a/src/lib/pages/migrate/components/MigrateContract.tsx +++ b/src/lib/pages/migrate/components/MigrateContract.tsx @@ -27,8 +27,8 @@ import { import { CodeSelectSection } from "lib/components/select-code"; import { useTxBroadcast } from "lib/hooks"; import { useSchemaStore } from "lib/providers/store"; -import type { CodeInfoResponseLcd } from "lib/services/types"; -import { useCodeInfoLcd } from "lib/services/wasm/code"; +import type { Code } from "lib/services/types"; +import { useCodeLcd } from "lib/services/wasm/code"; import type { BechAddr32, ComposedMsg } from "lib/types"; import { MsgType } from "lib/types"; import { composeMsg, isId, jsonValidate, resolvePermission } from "lib/utils"; @@ -120,15 +120,18 @@ export const MigrateContract = ({ }, }); - const { refetch } = useCodeInfoLcd(codeId, { + const { refetch } = useCodeLcd(Number(codeId), { enabled: false, retry: false, cacheTime: 0, - onSuccess(data) { - const permission = data.codeInfo.instantiatePermission; - setValue("codeHash", data.codeInfo.dataHash.toLowerCase()); + onSuccess: (data) => { + setValue("codeHash", data.hash.toLowerCase()); if ( - resolvePermission(address, permission.permission, permission.addresses) + resolvePermission( + address, + data.instantiatePermission, + data.permissionAddresses + ) ) setStatus({ state: "success" }); else { @@ -140,7 +143,7 @@ export const MigrateContract = ({ setSimulateError(""); } }, - onError() { + onError: () => { setStatus({ state: "error", message: "This code ID does not exist" }); setSimulateError(""); }, @@ -199,7 +202,8 @@ export const MigrateContract = ({ if (codeId.length) { setStatus({ state: "loading" }); const timer = setTimeout(() => { - refetch(); + if (isId(codeId)) refetch(); + else setStatus({ state: "error", message: "Invalid Code ID" }); }, 500); return () => clearTimeout(timer); } @@ -251,9 +255,9 @@ export const MigrateContract = ({ setValue("codeId", code); resetMsgInputSchema(); }} - setCodeHash={(data: CodeInfoResponseLcd) => { - setValue("codeHash", data.codeInfo.dataHash.toLowerCase()); - }} + setCodeHash={(data: Code) => + setValue("codeHash", data.hash.toLowerCase()) + } codeId={codeId} /> diff --git a/src/lib/services/contractStateService.ts b/src/lib/services/contractStateService.ts index c938e4a6e..174e6cc5a 100644 --- a/src/lib/services/contractStateService.ts +++ b/src/lib/services/contractStateService.ts @@ -13,7 +13,7 @@ export const useContractStates = ( return useInfiniteQuery( [ - CELATONE_QUERY_KEYS.CONTRACT_STATE, + CELATONE_QUERY_KEYS.CONTRACT_STATES, baseEndpoint, contractAddress, numStatesToLoad, diff --git a/src/lib/services/searchService.ts b/src/lib/services/searchService.ts index 83baea950..353ef8d75 100644 --- a/src/lib/services/searchService.ts +++ b/src/lib/services/searchService.ts @@ -24,7 +24,7 @@ import { usePoolByPoolId } from "./poolService"; import { useProposalData } from "./proposalService"; import { useTxData } from "./tx"; import { useValidatorData } from "./validator"; -import { useCodeDataByCodeId } from "./wasm/code"; +import { useCodeLcd } from "./wasm/code"; import { useContractLcd } from "./wasm/contract"; export type SearchResultType = @@ -117,9 +117,11 @@ export const useSearchHandler = ( }, [isAddr, contractData, icnsAddressData]); // Code - const { data: codeData, isFetching: codeFetching } = useCodeDataByCodeId( + const { data: codeData, isFetching: codeFetching } = useCodeLcd( Number(debouncedKeyword), - isWasm && isId(debouncedKeyword) + { + enabled: isWasm && isId(debouncedKeyword), + } ); // Tx diff --git a/src/lib/services/types/wasm/code.ts b/src/lib/services/types/wasm/code.ts index 719f7d282..5f9ca83ed 100644 --- a/src/lib/services/types/wasm/code.ts +++ b/src/lib/services/types/wasm/code.ts @@ -7,36 +7,35 @@ import { zPagination, zProjectInfo, zPublicCodeInfo, + zUtcDate, } from "lib/types"; -import { parseDate, parseTxHash, snakeToCamel } from "lib/utils"; +import { parseTxHash, snakeToCamel } from "lib/utils"; -export const zCodeInfoResponseLcd = z +const zCodesResponseItem = z .object({ - code_info: z.object({ - code_id: z.string(), - creator: zBechAddr, - data_hash: z.string(), - instantiate_permission: z.object({ - permission: z.nativeEnum(AccessConfigPermission), - address: zBechAddr.optional(), - addresses: z.array(zBechAddr).default([]), - }), - }), + id: z.number().nonnegative(), + cw2_contract: z.string().nullable(), + cw2_version: z.string().nullable(), + uploader: zBechAddr, + contract_count: z.number().nonnegative(), + instantiate_permission: z.nativeEnum(AccessConfigPermission), + permission_addresses: z.array(zBechAddr), }) - .transform(({ code_info: { instantiate_permission, ...rest } }) => ({ - codeInfo: { - ...snakeToCamel(rest), - instantiatePermission: { - permission: instantiate_permission.permission, - addresses: - instantiate_permission.address && - instantiate_permission.address !== "" - ? [instantiate_permission.address] - : instantiate_permission.addresses, - }, - }, + .transform((val) => ({ + id: val.id, + cw2Contract: val.cw2_contract, + cw2Version: val.cw2_version, + uploader: val.uploader, + contractCount: val.contract_count, + instantiatePermission: val.instantiate_permission, + permissionAddresses: val.permission_addresses, })); -export type CodeInfoResponseLcd = z.infer; + +export const zCodesResponse = z.object({ + items: z.array(zCodesResponseItem), + total: z.number().nonnegative(), +}); +export type CodesResponse = z.infer; const zCodesResponseItemLcd = z .object({ @@ -61,33 +60,6 @@ export const zCodesResponseLcd = z.object({ code_infos: z.array(zCodesResponseItemLcd), pagination: zPagination, }); -export type CodesResponseLcd = z.infer; - -const zCodesResponseItem = z - .object({ - id: z.number().nonnegative(), - cw2_contract: z.string().nullable(), - cw2_version: z.string().nullable(), - uploader: zBechAddr, - contract_count: z.number().nonnegative(), - instantiate_permission: z.nativeEnum(AccessConfigPermission), - permission_addresses: z.array(zBechAddr), - }) - .transform((val) => ({ - id: val.id, - cw2Contract: val.cw2_contract, - cw2Version: val.cw2_version, - uploader: val.uploader, - contractCount: val.contract_count, - instantiatePermission: val.instantiate_permission, - permissionAddresses: val.permission_addresses, - })); - -export const zCodesResponse = z.object({ - items: z.array(zCodesResponseItem), - total: z.number().nonnegative(), -}); -export type CodesResponse = z.infer; const zCode = z .object({ @@ -101,42 +73,22 @@ const zCode = z .object({ id: z.number().positive(), height: z.number().positive(), - created: z.string(), + created: zUtcDate, }) .nullable(), transaction: z .object({ height: z.number(), - hash: z.string(), - created: z.string(), + hash: z.string().transform((val) => parseTxHash(val).toUpperCase()), + created: zUtcDate, }) .nullable(), uploader: zBechAddr, }) .transform((val) => ({ - codeId: val.code_id, - cw2Contract: val.cw2_contract, - cw2Version: val.cw2_version, + ...snakeToCamel(val), hash: parseTxHash(val.hash), - instantiatePermission: val.instantiate_permission, - permissionAddresses: val.permission_addresses, - proposal: val.proposal - ? { - proposalId: val.proposal.id, - height: val.proposal.height, - created: parseDate(val.proposal.created), - } - : undefined, - transaction: val.transaction - ? { - height: val.transaction.height, - hash: parseTxHash(val.transaction.hash), - created: parseDate(val.transaction.created), - } - : undefined, - uploader: val.uploader, })); - export type Code = z.infer; export const zCodeData = z @@ -147,3 +99,35 @@ export const zCodeData = z }) .transform(snakeToCamel); export type CodeData = z.infer; + +export const zCodeInfoResponseLcd = z + .object({ + code_info: z.object({ + code_id: z.coerce.number(), + creator: zBechAddr, + data_hash: z.string(), + instantiate_permission: z.object({ + permission: z.nativeEnum(AccessConfigPermission), + address: zBechAddr.optional(), + addresses: z.array(zBechAddr).default([]), + }), + }), + }) + .transform( + ({ + code_info: { code_id, creator, data_hash, instantiate_permission }, + }) => ({ + codeId: code_id, + cw2Contract: null, + cw2Version: null, + hash: data_hash, + instantiatePermission: instantiate_permission.permission, + permissionAddresses: + instantiate_permission.address && instantiate_permission.address !== "" + ? [instantiate_permission.address] + : instantiate_permission.addresses, + proposal: null, + transaction: null, + uploader: creator, + }) + ); diff --git a/src/lib/services/types/wasm/contract.ts b/src/lib/services/types/wasm/contract.ts index 3a0fe6ff5..458585623 100644 --- a/src/lib/services/types/wasm/contract.ts +++ b/src/lib/services/types/wasm/contract.ts @@ -5,6 +5,7 @@ import { zBechAddr, zBechAddr32, zContractHistoryRemark, + zPagination, zProjectInfo, zPublicContractInfo, zUtcDate, @@ -63,6 +64,29 @@ export const zContractsResponse = z.object({ export type ContractsResponse = z.infer; +const zContractsResponseItemLcd = zBechAddr32.transform( + (val) => ({ + contractAddress: val, + label: "", + admin: undefined, + instantiator: undefined, + latestUpdated: undefined, + latestUpdater: undefined, + remark: undefined, + }) +); + +export const zContractsResponseLcd = z + .object({ + contracts: z.array(zContractsResponseItemLcd).default([]), // by code id case + contract_addresses: z.array(zContractsResponseItemLcd).optional(), + pagination: zPagination, + }) + .transform((val) => ({ + contracts: val.contract_addresses ?? val.contracts, + pagination: val.pagination, + })); + export const zContract = z .object({ address: zBechAddr32, diff --git a/src/lib/services/wasm/code/api.ts b/src/lib/services/wasm/code/api.ts index 7dfbf986e..1ff3fd76a 100644 --- a/src/lib/services/wasm/code/api.ts +++ b/src/lib/services/wasm/code/api.ts @@ -38,7 +38,7 @@ export const getCodesByAddress = async ( }) .then(({ data }) => zCodesResponse.parse(data)); -export const getCodeDataByCodeId = async ( +export const getCodeData = async ( endpoint: string, codeId: number, isGov: boolean diff --git a/src/lib/services/wasm/code/index.ts b/src/lib/services/wasm/code/index.ts index 9a58131fa..b7753a386 100644 --- a/src/lib/services/wasm/code/index.ts +++ b/src/lib/services/wasm/code/index.ts @@ -7,16 +7,11 @@ import { useGovConfig, useLcdEndpoint, } from "lib/app-provider"; -import type { - CodeData, - CodeInfoResponseLcd, - CodesResponse, -} from "lib/services/types"; +import type { Code, CodeData, CodesResponse } from "lib/services/types"; import type { BechAddr, BechAddr20, Option } from "lib/types"; -import { isId } from "lib/utils"; -import { getCodeDataByCodeId, getCodes, getCodesByAddress } from "./api"; -import { getCodeIdInfoLcd, getCodesLcd } from "./lcd"; +import { getCodeData, getCodes, getCodesByAddress } from "./api"; +import { getCodeLcd, getCodesLcd } from "./lcd"; export const useCodes = ( limit: number, @@ -47,13 +42,13 @@ export const useCodesLcd = () => { ); }; -export const useCodeDataByCodeId = (codeId: number, enabled = true) => { +export const useCodeData = (codeId: number, enabled = true) => { const { enabled: isGov } = useGovConfig({ shouldRedirect: false }); const endpoint = useBaseApiRoute("codes"); return useQuery( [CELATONE_QUERY_KEYS.CODE_DATA, endpoint, codeId, isGov], - async () => getCodeDataByCodeId(endpoint, codeId, isGov), + async () => getCodeData(endpoint, codeId, isGov), { retry: 1, refetchOnWindowFocus: false, @@ -62,21 +57,20 @@ export const useCodeDataByCodeId = (codeId: number, enabled = true) => { ); }; -export const useCodeInfoLcd = ( - codeId: string, - options?: Omit, "queryKey"> +export const useCodeLcd = ( + codeId: number, + options?: Omit, "queryKey"> ) => { - const endpoint = useBaseApiRoute("rest"); - - const queryFn = async () => { - if (!isId(codeId)) throw new Error("Invalid code ID"); - return getCodeIdInfoLcd(endpoint, Number(codeId)); - }; + const endpoint = useLcdEndpoint(); - return useQuery( - [CELATONE_QUERY_KEYS.CODE_INFO_LCD, endpoint, codeId], - queryFn, - options + return useQuery( + [CELATONE_QUERY_KEYS.CODE_DATA_LCD, endpoint, codeId], + async () => getCodeLcd(endpoint, codeId), + { + retry: 1, + refetchOnWindowFocus: false, + ...options, + } ); }; diff --git a/src/lib/services/wasm/code/lcd.ts b/src/lib/services/wasm/code/lcd.ts index 0706b17ce..aeb79599c 100644 --- a/src/lib/services/wasm/code/lcd.ts +++ b/src/lib/services/wasm/code/lcd.ts @@ -1,22 +1,13 @@ import axios from "axios"; -import type { CodeInfoResponseLcd, CodesResponseLcd } from "lib/services/types"; import { zCodeInfoResponseLcd, zCodesResponseLcd } from "lib/services/types"; import type { Option } from "lib/types"; import { parseWithError } from "lib/utils"; -export const getCodeIdInfoLcd = async ( - endpoint: string, - id: number -): Promise => - axios - .get(`${endpoint}/cosmwasm/wasm/v1/code/${id}`) - .then(({ data }) => parseWithError(zCodeInfoResponseLcd, data)); - export const getCodesLcd = async ( endpoint: string, paginationKey: Option -): Promise => +) => axios .get(`${endpoint}/cosmwasm/wasm/v1/code`, { params: { @@ -26,3 +17,8 @@ export const getCodesLcd = async ( }, }) .then(({ data }) => parseWithError(zCodesResponseLcd, data)); + +export const getCodeLcd = async (endpoint: string, codeId: number) => + axios + .get(`${endpoint}/cosmwasm/wasm/v1/code/${encodeURIComponent(codeId)}`) + .then(({ data }) => parseWithError(zCodeInfoResponseLcd, data)); diff --git a/src/lib/services/wasm/contract/index.ts b/src/lib/services/wasm/contract/index.ts index c3ded8dd2..3b60d4d98 100644 --- a/src/lib/services/wasm/contract/index.ts +++ b/src/lib/services/wasm/contract/index.ts @@ -1,5 +1,5 @@ import type { UseQueryOptions, UseQueryResult } from "@tanstack/react-query"; -import { useQuery } from "@tanstack/react-query"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; import { CELATONE_QUERY_KEYS, @@ -26,7 +26,11 @@ import { getInstantiatedContractsByAddress, getMigrationHistoriesByContractAddress, } from "./api"; -import { getContractLcd, getContractQueryLcd } from "./lcd"; +import { + getContractLcd, + getContractQueryLcd, + getContractsByCodeIdLcd, +} from "./lcd"; export const useContracts = ( limit: number, @@ -176,7 +180,7 @@ export const useContractsByCodeId = ( const endpoint = useBaseApiRoute("codes"); return useQuery( - [CELATONE_QUERY_KEYS.CONTRACTS_BY_CODE_ID, endpoint, limit, offset], + [CELATONE_QUERY_KEYS.CONTRACTS_BY_CODE_ID, endpoint, codeId, limit, offset], async () => getContractsByCodeId(endpoint, codeId, limit, offset), { retry: 1, @@ -186,6 +190,19 @@ export const useContractsByCodeId = ( ); }; +export const useContractsByCodeIdLcd = (codeId: number) => { + const endpoint = useLcdEndpoint(); + + return useInfiniteQuery( + [CELATONE_QUERY_KEYS.CONTRACTS_BY_CODE_ID_LCD, endpoint, codeId], + ({ pageParam }) => getContractsByCodeIdLcd(endpoint, codeId, pageParam), + { + getNextPageParam: (lastPage) => lastPage.pagination.nextKey ?? undefined, + refetchOnWindowFocus: false, + } + ); +}; + export const useContractQueryLcd = ( contractAddress: BechAddr32, msg: string, diff --git a/src/lib/services/wasm/contract/lcd.ts b/src/lib/services/wasm/contract/lcd.ts index 5f8be4db0..738e3f818 100644 --- a/src/lib/services/wasm/contract/lcd.ts +++ b/src/lib/services/wasm/contract/lcd.ts @@ -1,7 +1,7 @@ import axios from "axios"; -import { zContractLcd } from "lib/services/types"; -import type { BechAddr32, JsonDataType } from "lib/types"; +import { zContractLcd, zContractsResponseLcd } from "lib/services/types"; +import type { BechAddr32, JsonDataType, Option } from "lib/types"; import { encode, parseWithError } from "lib/utils"; export const getContractQueryLcd = ( @@ -19,3 +19,21 @@ export const getContractLcd = (endpoint: string, contractAddress: BechAddr32) => axios( `${endpoint}/cosmwasm/wasm/v1/contract/${encodeURI(contractAddress)}` ).then(({ data }) => parseWithError(zContractLcd, data)); + +export const getContractsByCodeIdLcd = ( + endpoint: string, + codeId: number, + paginationKey: Option +) => + axios + .get( + `${endpoint}/cosmwasm/wasm/v1/code/${encodeURIComponent(codeId)}/contracts`, + { + params: { + "pagination.limit": 10, + "pagination.reverse": true, + "pagination.key": paginationKey, + }, + } + ) + .then(({ data }) => parseWithError(zContractsResponseLcd, data)); diff --git a/src/lib/stores/contract.ts b/src/lib/stores/contract.ts index 7ba2aeaf6..0af5a843e 100644 --- a/src/lib/stores/contract.ts +++ b/src/lib/stores/contract.ts @@ -15,7 +15,7 @@ import { formatSlugName, getCurrentDate, getTagsDefault } from "lib/utils"; export interface ContractLocalInfo { contractAddress: BechAddr32; instantiator: Option; - label: string; + label: string; // NOTE: if empty means no label provided name?: string; description?: string; tags?: string[];