diff --git a/CHANGELOG.md b/CHANGELOG.md index 521721932..33f6a6c14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,9 +39,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#698](https://github.com/alleslabs/celatone-frontend/pull/698) Add ledger + ### Improvements - [#703](https://github.com/alleslabs/celatone-frontend/pull/703) api v1 - contract's query msgs +- [#697](https://github.com/alleslabs/celatone-frontend/pull/697) api v1 - contract tables (migrations and related proposals) - [#696](https://github.com/alleslabs/celatone-frontend/pull/696) api v1 - block details - [#695](https://github.com/alleslabs/celatone-frontend/pull/695) api v1 - contract states - [#678](https://github.com/alleslabs/celatone-frontend/pull/678) api v1 - contract table counts diff --git a/src/lib/app-provider/env.ts b/src/lib/app-provider/env.ts index 9977ea488..e9a161f85 100644 --- a/src/lib/app-provider/env.ts +++ b/src/lib/app-provider/env.ts @@ -42,7 +42,7 @@ export enum CELATONE_QUERY_KEYS { INSTANTIATED_COUNT_BY_WALLET_ADDRESS = "CELATONE_QUERY_INSTANTIATED_COUNT_BY_WALLET_ADDRESS", INSTANTIATED_LIST_BY_WALLET_ADDRESS = "CELATONE_QUERY_INSTANTIATED_LIST_BY_WALLET_ADDRESS", ADMINS_BY_CONTRACTS = "CELATONE_QUERY_ADMINS_BY_CONTRACTS", - CONTRACT_MIGRATION_HISTORIES_PAGINATION = "CELATONE_QUERY_CONTRACT_MIGRATION_HISTORIES_PAGINATION", + CONTRACT_MIGRATION_HISTORIES_BY_CONTRACT_ADDRESS = "CELATONE_QUERY_CONTRACT_MIGRATION_HISTORIES_BY_CONTRACT_ADDRESS", CONTRACTS_BY_CODE_ID_PAGINATION = "CELATONE_QUERY_CONTRACTS_BY_CODE_ID_PAGINATION", CONTRACTS_BY_CODE_ID_COUNT = "CELATONE_QUERY_CONTRACTS_BY_CODE_ID_COUNT", // X/STAKING @@ -52,7 +52,7 @@ export enum CELATONE_QUERY_KEYS { // FAUCET FAUCET_INFO = "CELATONE_QUERY_FAUCET_INFO", // X/GOV - RELATED_PROPOSALS_BY_CONTRACT_ADDRESS_PAGINATION = "CELATONE_QUERY_RELATED_PROPOSALS_BY_CONTRACT_ADDRESS_PAGINATION", + RELATED_PROPOSALS_BY_CONTRACT_ADDRESS = "CELATONE_QUERY_RELATED_PROPOSALS_BY_CONTRACT_ADDRESS", PROPOSALS_BY_MODULE_ID = "CELATONE_QUERY_PROPOSALS_BY_MODULE_ID", PROPOSALS_COUNT_BY_MODULE_ID = "CELATONE_QUERY_PROPOSALS_COUNT_BY_MODULE_ID", PROPOSALS_BY_ADDRESS = "CELATONE_QUERY_PROPOSALS_BY_ADDRESS", diff --git a/src/lib/app-provider/hooks/index.ts b/src/lib/app-provider/hooks/index.ts index 29afb13d7..89ef0f329 100644 --- a/src/lib/app-provider/hooks/index.ts +++ b/src/lib/app-provider/hooks/index.ts @@ -8,6 +8,7 @@ export * from "./useMediaQuery"; export * from "./useNetworkChange"; export * from "./useRestrictedInput"; export * from "./useSelectChain"; +export * from "./useGetSigningClient"; export * from "./useBaseApiRoute"; export * from "./useRPCEndpoint"; export * from "./useConfig"; diff --git a/src/lib/app-provider/hooks/useGetSigningClient.ts b/src/lib/app-provider/hooks/useGetSigningClient.ts new file mode 100644 index 000000000..fd49566d2 --- /dev/null +++ b/src/lib/app-provider/hooks/useGetSigningClient.ts @@ -0,0 +1,63 @@ +/* eslint-disable */ +import type { WalletClient } from "@cosmos-kit/core"; + +import { useCurrentChain } from "./useCurrentChain"; +import { useCallback } from "react"; +import { useWalletClient } from "@cosmos-kit/react"; +import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; +import { useRPCEndpoint } from "./useRPCEndpoint"; +import { getCustomedSigningCosmwasm } from "lib/providers/cosmos-kit/options"; + +type MergedWalletClient = + | WalletClient + // | import("@cosmos-kit/cosmostation-extension/cjs/extension/client").CosmostationClient + | import("@cosmos-kit/keplr-extension/cjs/extension/client").KeplrClient + | import("@cosmos-kit/station-extension/cjs/extension/client").StationClient; + +export const isLedger = async ( + walletClient: T, + chainID: string +) => { + // mostly everything else + if ("client" in walletClient && "getKey" in walletClient.client) { + const key = await walletClient.client.getKey(chainID); + return key.isNanoLedger; + } + + // cosmostation + // if ("client" in walletClient && "cosmos" in walletClient.client) { + // const account = await walletClient.client.cosmos.request({ + // method: "cos_account", + // params: { chainName: chainID }, + // }); + // return Boolean(account.isLedger); + // } + + return false; +}; + +export const useGetSigningClient = () => { + const { client: walletClient } = useWalletClient(); + const { + chain: { chain_id: chainId }, + getSigningCosmWasmClient, + } = useCurrentChain(); + const rpcEndpoint = useRPCEndpoint(); + + return useCallback(async () => { + if (walletClient && (await isLedger(walletClient, chainId))) { + const signer = + walletClient.getOfflineSignerAmino?.(chainId) ?? + (await walletClient.getOfflineSigner?.(chainId, "amino")); + + if (!signer || !("signAmino" in signer)) return undefined; + + return SigningCosmWasmClient.connectWithSigner( + rpcEndpoint, + signer, + getCustomedSigningCosmwasm() + ); + } + return await getSigningCosmWasmClient(); + }, [chainId, rpcEndpoint, JSON.stringify(walletClient)]); +}; diff --git a/src/lib/app-provider/queries/simulateFee.ts b/src/lib/app-provider/queries/simulateFee.ts index 535fcafa8..36fbae427 100644 --- a/src/lib/app-provider/queries/simulateFee.ts +++ b/src/lib/app-provider/queries/simulateFee.ts @@ -5,7 +5,12 @@ import type { UseQueryOptions } from "@tanstack/react-query"; import { gzip } from "node-gzip"; import { CELATONE_QUERY_KEYS } from "../env"; -import { useCurrentChain, useDummyWallet, useRPCEndpoint } from "../hooks"; +import { + useCurrentChain, + useDummyWallet, + useGetSigningClient, + useRPCEndpoint, +} from "../hooks"; import type { AccessType, Addr, @@ -33,7 +38,8 @@ export const useSimulateFeeQuery = ({ onSuccess, onError, }: SimulateQueryParams) => { - const { address, getSigningCosmWasmClient, chain } = useCurrentChain(); + const { address, chain } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); const { dummyWallet, dummyAddress } = useDummyWallet(); const rpcEndpoint = useRPCEndpoint(); const userAddress = isDummyUser ? dummyAddress : address || dummyAddress; @@ -50,7 +56,7 @@ export const useSimulateFeeQuery = ({ rpcEndpoint, dummyWallet ) - : await getSigningCosmWasmClient(); + : await getSigningClient(); if (!client) { throw new Error("Fail to get SigningCosmWasmClient"); diff --git a/src/lib/app-provider/tx/clearAdmin.ts b/src/lib/app-provider/tx/clearAdmin.ts index 2881a260a..4480f05d3 100644 --- a/src/lib/app-provider/tx/clearAdmin.ts +++ b/src/lib/app-provider/tx/clearAdmin.ts @@ -2,7 +2,12 @@ import { useQueryClient } from "@tanstack/react-query"; import { useCallback } from "react"; import { CELATONE_QUERY_KEYS } from "../env"; -import { useCurrentChain, useFabricateFee, useWasmConfig } from "../hooks"; +import { + useCurrentChain, + useFabricateFee, + useGetSigningClient, + useWasmConfig, +} from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { clearAdminTx } from "lib/app-fns/tx/clearAdmin"; import type { ContractAddr, HumanAddr } from "lib/types"; @@ -12,14 +17,15 @@ export interface ClearAdminStreamParams { } export const useClearAdminTx = (contractAddress: ContractAddr) => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); const queryClient = useQueryClient(); const fabricateFee = useFabricateFee(); const wasm = useWasmConfig({ shouldRedirect: false }); return useCallback( async ({ onTxSucceed }: ClearAdminStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); @@ -50,12 +56,12 @@ export const useClearAdminTx = (contractAddress: ContractAddr) => { }); }, [ - getSigningCosmWasmClient, address, - wasm, - fabricateFee, contractAddress, + fabricateFee, + getSigningClient, queryClient, + wasm, ] ); }; diff --git a/src/lib/app-provider/tx/execute.ts b/src/lib/app-provider/tx/execute.ts index cf1de6790..1216852ac 100644 --- a/src/lib/app-provider/tx/execute.ts +++ b/src/lib/app-provider/tx/execute.ts @@ -1,7 +1,7 @@ import type { Coin, StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { executeContractTx } from "lib/app-fns/tx/execute"; import type { Activity } from "lib/stores/contract"; @@ -17,7 +17,8 @@ export interface ExecuteStreamParams { } export const useExecuteContractTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -28,7 +29,7 @@ export const useExecuteContractTx = () => { msg, funds, }: ExecuteStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -47,6 +48,6 @@ export const useExecuteContractTx = () => { onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/instantiate.ts b/src/lib/app-provider/tx/instantiate.ts index d081f2330..094f129d0 100644 --- a/src/lib/app-provider/tx/instantiate.ts +++ b/src/lib/app-provider/tx/instantiate.ts @@ -2,7 +2,7 @@ import type { InstantiateResult } from "@cosmjs/cosmwasm-stargate"; import type { Coin, StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { instantiateContractTx } from "lib/app-fns/tx/instantiate"; @@ -18,7 +18,8 @@ export interface InstantiateStreamParams { } export const useInstantiateTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -31,7 +32,7 @@ export const useInstantiateTx = () => { onTxSucceed, onTxFailed, }: InstantiateStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -52,6 +53,6 @@ export const useInstantiateTx = () => { onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/migrate.ts b/src/lib/app-provider/tx/migrate.ts index cef562c7a..195dfc010 100644 --- a/src/lib/app-provider/tx/migrate.ts +++ b/src/lib/app-provider/tx/migrate.ts @@ -1,7 +1,7 @@ import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { migrateContractTx } from "lib/app-fns/tx/migrate"; import type { ContractAddr, HumanAddr, Option } from "lib/types"; @@ -16,7 +16,8 @@ export interface MigrateStreamParams { } export const useMigrateTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -27,7 +28,7 @@ export const useMigrateTx = () => { onTxSucceed, onTxFailed, }: MigrateStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -46,6 +47,6 @@ export const useMigrateTx = () => { onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/move/executeModule.ts b/src/lib/app-provider/tx/move/executeModule.ts index 2d7b3babc..67ad79f03 100644 --- a/src/lib/app-provider/tx/move/executeModule.ts +++ b/src/lib/app-provider/tx/move/executeModule.ts @@ -4,7 +4,7 @@ import { useCallback } from "react"; import { trackTxSucceed } from "lib/amplitude"; import { executeModuleTx } from "lib/app-fns/tx/move/executeModule"; -import { useCurrentChain } from "lib/app-provider/hooks"; +import { useCurrentChain, useGetSigningClient } from "lib/app-provider/hooks"; import type { HexAddr, HumanAddr } from "lib/types"; import { toEncodeObject } from "lib/utils"; @@ -20,7 +20,8 @@ export interface ExecuteModuleStreamParams { } export const useExecuteModuleTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -33,7 +34,7 @@ export const useExecuteModuleTx = () => { onTxSucceed, onTxFailed, }: ExecuteModuleStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); @@ -61,6 +62,6 @@ export const useExecuteModuleTx = () => { onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/publish.tsx b/src/lib/app-provider/tx/publish.tsx index 4f1a1f5c0..199cc3cac 100644 --- a/src/lib/app-provider/tx/publish.tsx +++ b/src/lib/app-provider/tx/publish.tsx @@ -2,7 +2,7 @@ import type { EncodeObject } from "@cosmjs/proto-signing"; import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { publishModuleTx } from "lib/app-fns/tx/publish"; import type { HumanAddr } from "lib/types"; @@ -24,7 +24,8 @@ export interface PublishModuleStreamParams { } export const usePublishModuleTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -33,7 +34,7 @@ export const usePublishModuleTx = () => { estimatedFee, messages, }: PublishModuleStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -49,6 +50,6 @@ export const usePublishModuleTx = () => { messages, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/resend.ts b/src/lib/app-provider/tx/resend.ts index 7dc4379e0..623491aac 100644 --- a/src/lib/app-provider/tx/resend.ts +++ b/src/lib/app-provider/tx/resend.ts @@ -2,7 +2,7 @@ import type { EncodeObject } from "@cosmjs/proto-signing"; import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { resendTx } from "lib/app-fns/tx/resend"; import type { HumanAddr } from "lib/types"; @@ -15,7 +15,8 @@ export interface ResendStreamParams { } export const useResendTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -24,7 +25,7 @@ export const useResendTx = () => { estimatedFee, messages, }: ResendStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -40,6 +41,6 @@ export const useResendTx = () => { onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/script.tsx b/src/lib/app-provider/tx/script.tsx index 689a48689..e7a24c41e 100644 --- a/src/lib/app-provider/tx/script.tsx +++ b/src/lib/app-provider/tx/script.tsx @@ -2,7 +2,7 @@ import type { EncodeObject } from "@cosmjs/proto-signing"; import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { deployScriptTx } from "lib/app-fns/tx/script"; import type { HumanAddr } from "lib/types"; @@ -15,7 +15,8 @@ export interface DeployScriptStreamParams { } export const useDeployScriptTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -24,7 +25,7 @@ export const useDeployScriptTx = () => { estimatedFee, messages, }: DeployScriptStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -40,6 +41,6 @@ export const useDeployScriptTx = () => { messages, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/submitProposal.ts b/src/lib/app-provider/tx/submitProposal.ts index 159bda2c5..7fa78a883 100644 --- a/src/lib/app-provider/tx/submitProposal.ts +++ b/src/lib/app-provider/tx/submitProposal.ts @@ -2,7 +2,7 @@ import type { EncodeObject } from "@cosmjs/proto-signing"; import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { submitStoreCodeProposalTx, @@ -20,7 +20,8 @@ export interface SubmitWhitelistProposalStreamParams { } export const useSubmitWhitelistProposalTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -31,7 +32,7 @@ export const useSubmitWhitelistProposalTx = () => { whitelistNumber, amountToVote, }: SubmitWhitelistProposalStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -49,7 +50,7 @@ export const useSubmitWhitelistProposalTx = () => { onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/updateAdmin.ts b/src/lib/app-provider/tx/updateAdmin.ts index 008c46d99..7bbfe6203 100644 --- a/src/lib/app-provider/tx/updateAdmin.ts +++ b/src/lib/app-provider/tx/updateAdmin.ts @@ -1,7 +1,7 @@ import type { StdFee } from "@cosmjs/stargate"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { updateAdminTx } from "lib/app-fns/tx/updateAdmin"; import type { Addr, ContractAddr, HumanAddr, Option } from "lib/types"; @@ -15,7 +15,8 @@ export interface UpdateAdminStreamParams { } export const useUpdateAdminTx = () => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -25,7 +26,7 @@ export const useUpdateAdminTx = () => { onTxSucceed, onTxFailed, }: UpdateAdminStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!estimatedFee) return null; @@ -43,6 +44,6 @@ export const useUpdateAdminTx = () => { onTxFailed, }); }, - [address, getSigningCosmWasmClient] + [address, getSigningClient] ); }; diff --git a/src/lib/app-provider/tx/upload.ts b/src/lib/app-provider/tx/upload.ts index 7e121872f..21d311b29 100644 --- a/src/lib/app-provider/tx/upload.ts +++ b/src/lib/app-provider/tx/upload.ts @@ -2,7 +2,7 @@ import type { StdFee } from "@cosmjs/stargate"; import { gzip } from "node-gzip"; import { useCallback } from "react"; -import { useCurrentChain } from "../hooks"; +import { useCurrentChain, useGetSigningClient } from "../hooks"; import { trackTxSucceed } from "lib/amplitude"; import { uploadContractTx } from "lib/app-fns/tx/upload"; import type { AccessType, Addr, HumanAddr, Option } from "lib/types"; @@ -29,7 +29,8 @@ export interface UploadStreamParams { } export const useUploadContractTx = (isMigrate: boolean) => { - const { address, getSigningCosmWasmClient } = useCurrentChain(); + const { address } = useCurrentChain(); + const getSigningClient = useGetSigningClient(); return useCallback( async ({ @@ -41,7 +42,7 @@ export const useUploadContractTx = (isMigrate: boolean) => { estimatedFee, onTxSucceed, }: UploadStreamParams) => { - const client = await getSigningCosmWasmClient(); + const client = await getSigningClient(); if (!address || !client) throw new Error("Please check your wallet connection."); if (!wasmFileName || !wasmCode || !estimatedFee) return null; @@ -67,6 +68,6 @@ export const useUploadContractTx = (isMigrate: boolean) => { }, }); }, - [address, getSigningCosmWasmClient, isMigrate] + [address, getSigningClient, isMigrate] ); }; diff --git a/src/lib/pages/contract-details/components/tables/RelatedProposalsTable.tsx b/src/lib/pages/contract-details/components/tables/RelatedProposalsTable.tsx index 38c44a3dc..2541ed62e 100644 --- a/src/lib/pages/contract-details/components/tables/RelatedProposalsTable.tsx +++ b/src/lib/pages/contract-details/components/tables/RelatedProposalsTable.tsx @@ -4,7 +4,7 @@ import { Pagination } from "lib/components/pagination"; import { usePaginator } from "lib/components/pagination/usePaginator"; import { EmptyState, ErrorFetching } from "lib/components/state"; import { ProposalsTable } from "lib/components/table"; -import { useRelatedProposalsByContractAddressPagination } from "lib/services/proposalService"; +import { useRelatedProposalsByContractAddress } from "lib/services/proposalService"; import type { ContractAddr, Option } from "lib/types"; interface RelatedProposalsTableProps { @@ -40,11 +40,7 @@ export const RelatedProposalsTable = ({ data: relatedProposals, isLoading, error, - } = useRelatedProposalsByContractAddressPagination( - contractAddress, - offset, - pageSize - ); + } = useRelatedProposalsByContractAddress(contractAddress, offset, pageSize); const onPageChange = (nextPage: number) => { refetchCount(); @@ -61,7 +57,7 @@ export const RelatedProposalsTable = ({ return ( <> { const { operation, type, value } = remark; - if (type === "genesis") return Genesis; + if (type === "genesis" || isUndefined(value)) + return Genesis; const isGovernance = type === "governance"; const prefix = capitalize(operation.split("_").pop()); diff --git a/src/lib/pages/contract-details/components/tables/migration/index.tsx b/src/lib/pages/contract-details/components/tables/migration/index.tsx index 70d45e1bf..1f9d946bd 100644 --- a/src/lib/pages/contract-details/components/tables/migration/index.tsx +++ b/src/lib/pages/contract-details/components/tables/migration/index.tsx @@ -1,9 +1,8 @@ -import type { ChangeEvent } from "react"; - import { useMobile } from "lib/app-provider"; +import { Loading } from "lib/components/Loading"; import { Pagination } from "lib/components/pagination"; import { usePaginator } from "lib/components/pagination/usePaginator"; -import { EmptyState } from "lib/components/state"; +import { EmptyState, ErrorFetching } from "lib/components/state"; import { MobileTableContainer, TableContainer } from "lib/components/table"; import { useMigrationHistories } from "lib/pages/contract-details/data"; import type { ContractAddr, Option } from "lib/types"; @@ -42,26 +41,15 @@ export const MigrationTable = ({ }, }); - // TODO: loading state - const migrationHistories = useMigrationHistories( + const { data, isLoading, error } = useMigrationHistories( contractAddress, offset, pageSize ); - const onPageChange = (nextPage: number) => { - refetchCount(); - setCurrentPage(nextPage); - }; - - const onPageSizeChange = (e: ChangeEvent) => { - const size = Number(e.target.value); - refetchCount(); - setPageSize(size); - setCurrentPage(1); - }; - - if (!migrationHistories?.length) + if (isLoading) return ; + if (error) return ; + if (!data?.items.length) return ( {isMobile ? ( - {migrationHistories.map((history, idx) => ( + {data.items.map((history, idx) => ( - {migrationHistories.map((history, idx) => ( + {data.items.map((history, idx) => ( { + refetchCount(); + setCurrentPage(nextPage); + }} + onPageSizeChange={(e) => { + const size = Number(e.target.value); + refetchCount(); + setPageSize(size); + setCurrentPage(1); + }} /> )} diff --git a/src/lib/pages/contract-details/data.ts b/src/lib/pages/contract-details/data.ts index b1676c01a..10198afa0 100644 --- a/src/lib/pages/contract-details/data.ts +++ b/src/lib/pages/contract-details/data.ts @@ -12,13 +12,13 @@ import { queryContract, queryContractCw2Info } from "lib/services/contract"; import { useContractDetailByContractAddress, useInstantiateDetailByContractQuery, - useMigrationHistoriesByContractAddressPagination, + useMigrationHistoriesByContractAddress, } from "lib/services/contractService"; import { usePublicProjectByContractAddress, usePublicProjectBySlug, } from "lib/services/publicProjectService"; -import type { ContractAddr, ContractMigrationHistory, Option } from "lib/types"; +import type { ContractAddr, ContractMigrationHistory } from "lib/types"; import { coinToTokenWithValue, compareTokenWithValues } from "lib/utils"; import type { ContractData } from "./types"; @@ -101,22 +101,23 @@ export const useMigrationHistories = ( contractAddress: ContractAddr, offset: number, pageSize: number -): Option => { - const { data: migrationData } = - useMigrationHistoriesByContractAddressPagination( - contractAddress, - offset, - pageSize - ); +) => { + const { data, ...res } = useMigrationHistoriesByContractAddress( + contractAddress, + offset, + pageSize + ); const { getCodeLocalInfo } = useCodeStore(); - if (!migrationData) return undefined; - - return migrationData.map((data) => { - const localInfo = getCodeLocalInfo(data.codeId); - return { - ...data, - codeName: localInfo?.name, - }; - }); + return { + data: data + ? { + items: data.items.map((migration) => ({ + ...migration, + codeName: getCodeLocalInfo(migration.codeId)?.name, + })), + } + : undefined, + ...res, + }; }; diff --git a/src/lib/providers/cosmos-kit/move.ts b/src/lib/providers/cosmos-kit/move.ts index 7cd51e50b..e9868e8ef 100644 --- a/src/lib/providers/cosmos-kit/move.ts +++ b/src/lib/providers/cosmos-kit/move.ts @@ -1,4 +1,10 @@ import type { GeneratedType } from "@cosmjs/proto-signing"; +import type { AminoConverters } from "@cosmjs/stargate"; +import { + MsgExecute as MsgExecuteClass, + MsgPublish as MsgPublishClass, + MsgScript as MsgScriptClass, +} from "@initia/initia.js"; import { MsgExecute, MsgPublish, @@ -10,3 +16,38 @@ export const moveTypes: ReadonlyArray<[string, GeneratedType]> = [ ["/initia.move.v1.MsgPublish", MsgPublish], ["/initia.move.v1.MsgScript", MsgScript], ]; + +export const createMoveAminoConverters = (): AminoConverters => { + return { + "/initia.move.v1.MsgExecute": { + aminoType: "move/MsgExecute", + toAmino: (data: MsgExecuteClass.Proto) => + MsgExecuteClass.fromProto(data).toAmino().value, + fromAmino: (data: MsgExecuteClass.Amino["value"]) => + MsgExecuteClass.fromAmino({ + type: "move/MsgExecute", + value: data, + }).toProto(), + }, + "/initia.move.v1.MsgPublish": { + aminoType: "move/MsgPublish", + toAmino: (data: MsgPublishClass.Proto) => + MsgPublishClass.fromProto(data).toAmino().value, + fromAmino: (data: MsgPublishClass.Amino["value"]) => + MsgPublishClass.fromAmino({ + type: "move/MsgPublish", + value: data, + }).toProto(), + }, + "/initia.move.v1.MsgScript": { + aminoType: "move/MsgScript", + toAmino: (data: MsgScriptClass.Proto) => + MsgScriptClass.fromProto(data).toAmino().value, + fromAmino: (data: MsgScriptClass.Amino["value"]) => + MsgScriptClass.fromAmino({ + type: "move/MsgScript", + value: data, + }).toProto(), + }, + }; +}; diff --git a/src/lib/providers/cosmos-kit/options.ts b/src/lib/providers/cosmos-kit/options.ts index d4e90c238..ebacbb8c9 100644 --- a/src/lib/providers/cosmos-kit/options.ts +++ b/src/lib/providers/cosmos-kit/options.ts @@ -10,7 +10,7 @@ import { defaultRegistryTypes as defaultStargateTypes, } from "@cosmjs/stargate"; -import { moveTypes } from "./move"; +import { createMoveAminoConverters, moveTypes } from "./move"; /** * Remark: This is the custom signerOptions for the SigningCosmWasmClient. @@ -20,7 +20,7 @@ export const getCustomedSigningCosmwasm = (): SigningCosmWasmClientOptions => { const aminoTypes = { ...createDefaultAminoConverters(), ...createWasmAminoConverters(), - // TODO: move amino converters to be implemented + ...createMoveAminoConverters(), }; return { diff --git a/src/lib/query/contract.ts b/src/lib/query/contract.ts index bb1580d96..c197b4866 100644 --- a/src/lib/query/contract.ts +++ b/src/lib/query/contract.ts @@ -183,35 +183,3 @@ export const getContractListCountByCodeId = graphql(` } } `); - -export const getMigrationHistoriesByContractAddressPagination = graphql(` - query getMigrationHistoriesByContractAddress( - $contractAddress: String! - $offset: Int! - $pageSize: Int! - ) { - contract_histories( - where: { contract: { address: { _eq: $contractAddress } } } - order_by: { block: { timestamp: desc } } - limit: $pageSize - offset: $offset - ) { - code_id - account { - address - } - block { - height - timestamp - } - remark - code { - account { - address - } - cw2_contract - cw2_version - } - } - } -`); diff --git a/src/lib/query/proposal.ts b/src/lib/query/proposal.ts index 012e5ab3f..c592e6a76 100644 --- a/src/lib/query/proposal.ts +++ b/src/lib/query/proposal.ts @@ -1,34 +1,5 @@ import { graphql } from "lib/gql"; -export const getRelatedProposalsByContractAddressPagination = graphql(` - query getRelatedProposalsByContractAddressPagination( - $contractAddress: String! - $offset: Int! - $pageSize: Int! - ) { - contract_proposals( - where: { contract: { address: { _eq: $contractAddress } } } - order_by: { proposal_id: desc } - offset: $offset - limit: $pageSize - ) { - proposal { - title - status - voting_end_time - deposit_end_time - type - account { - address - } - is_expedited - } - proposal_id - resolved_height - } - } -`); - export const getRelatedProposalsByModuleIdPagination = graphql(` query getRelatedProposalsByModuleIdPagination( $moduleId: Int! diff --git a/src/lib/services/account.ts b/src/lib/services/account.ts index 2d312645a..fa21a0f94 100644 --- a/src/lib/services/account.ts +++ b/src/lib/services/account.ts @@ -29,7 +29,7 @@ export const getAccountInfo = async ( ): Promise => axios .get(`${endpoint}/${encodeURIComponent(address)}/info`) - .then((res) => zAccountInfo.parse(res.data)); + .then(({ data }) => zAccountInfo.parse(data)); const zAccountTableCounts = z .object({ @@ -56,4 +56,4 @@ export const getAccountTableCounts = async ( is_wasm: isWasm, }, }) - .then((res) => zAccountTableCounts.parse(res.data)); + .then(({ data }) => zAccountTableCounts.parse(data)); diff --git a/src/lib/services/asset.ts b/src/lib/services/asset.ts index f4184d003..5df6b3b0c 100644 --- a/src/lib/services/asset.ts +++ b/src/lib/services/asset.ts @@ -14,4 +14,4 @@ export const getAssetInfos = async ( with_prices: withPrices, }, }) - .then((res) => z.array(zAssetInfo).parse(res.data)); + .then(({ data }) => z.array(zAssetInfo).parse(data)); diff --git a/src/lib/services/balance.ts b/src/lib/services/balance.ts index bacf6644d..8657450f9 100644 --- a/src/lib/services/balance.ts +++ b/src/lib/services/balance.ts @@ -19,4 +19,4 @@ export const getBalances = async ( ): Promise => axios .get(`${endpoint}/${encodeURIComponent(address)}/balances`) - .then((res) => zBalancesResponse.parse(res.data)); + .then(({ data }) => zBalancesResponse.parse(data)); diff --git a/src/lib/services/code.ts b/src/lib/services/code.ts index 248837306..1e9210bc1 100644 --- a/src/lib/services/code.ts +++ b/src/lib/services/code.ts @@ -72,4 +72,4 @@ export const getCodesByAddress = async ( offset, }, }) - .then((res) => zCodesResponse.parse(res.data)); + .then(({ data }) => zCodesResponse.parse(data)); diff --git a/src/lib/services/contract.ts b/src/lib/services/contract.ts index 278509bca..a5ab35d35 100644 --- a/src/lib/services/contract.ts +++ b/src/lib/services/contract.ts @@ -5,10 +5,14 @@ import type { Addr, ContractAddr, ContractInfo, - ContractHistoryRemark, - RemarkType, + ContractMigrationHistory, +} from "lib/types"; +import { + zAddr, + zContractAddr, + zContractHistoryRemark, + zUtcDate, } from "lib/types"; -import { RemarkOperation, zAddr, zContractAddr, zUtcDate } from "lib/types"; import { encode, libDecode, snakeToCamel } from "lib/utils"; interface ContractCw2InfoRaw { @@ -76,17 +80,7 @@ const zContractsResponseItem = z instantiator: zAddr, latest_updated: zUtcDate, latest_updater: zAddr, - remark: z - .object({ - operation: z.nativeEnum(RemarkOperation), - type: z.string(), - value: z.string(), - }) - .transform((val) => ({ - operation: val.operation, - type: val.type as RemarkType, // TODO: remove type assertion, - value: val.value, - })), + remark: zContractHistoryRemark, }) .transform((val) => ({ contractAddress: val.contract_address, @@ -138,6 +132,51 @@ export const getAdminContractsByAddress = async ( }) .then(({ data }) => zContractsResponse.parse(data)); +const zMigrationHistoriesResponseItem = z + .object({ + code_id: z.number().positive(), + cw2_contract: z.string().nullable(), + cw2_version: z.string().nullable(), + height: z.number(), + remark: zContractHistoryRemark, + sender: zAddr, + timestamp: zUtcDate, + uploader: zAddr, + }) + .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, + })); + +const zMigrationHistoriesResponse = z.object({ + items: z.array(zMigrationHistoriesResponseItem), +}); + +export type MigrationHistoriesResponse = z.infer< + typeof zMigrationHistoriesResponse +>; + +export const getMigrationHistoriesByContractAddress = async ( + endpoint: string, + contractAddress: ContractAddr, + limit: number, + offset: number +) => + axios + .get(`${endpoint}/${encodeURIComponent(contractAddress)}/migrations`, { + params: { + limit, + offset, + }, + }) + .then(({ data }) => zMigrationHistoriesResponse.parse(data)); + const zContractTableCounts = z .object({ tx: z.number().nullish(), diff --git a/src/lib/services/contractService.ts b/src/lib/services/contractService.ts index 54887bee2..b304f3ab6 100644 --- a/src/lib/services/contractService.ts +++ b/src/lib/services/contractService.ts @@ -19,12 +19,10 @@ import { getInstantiatedCountByUserQueryDocument, getInstantiateDetailByContractQueryDocument, getInstantiatedListByUserQueryDocument, - getMigrationHistoriesByContractAddressPagination, } from "lib/query"; import type { ContractLocalInfo } from "lib/stores/contract"; import type { ContractAddr, - ContractMigrationHistory, HumanAddr, Option, Dict, @@ -32,7 +30,7 @@ import type { ContractInfo, Nullable, } from "lib/types"; -import { parseDate, parseTxHashOpt, parseDateOpt } from "lib/utils"; +import { parseTxHashOpt, parseDateOpt } from "lib/utils"; import { getCodeIdInfo } from "./code"; import { @@ -40,8 +38,13 @@ import { getContractQueryMsgs, getContractTableCounts, getInstantiatedContractsByAddress, + getMigrationHistoriesByContractAddress, +} from "./contract"; +import type { + ContractTableCounts, + ContractsResponse, + MigrationHistoriesResponse, } from "./contract"; -import type { ContractTableCounts, ContractsResponse } from "./contract"; export interface ContractDetail extends ContractLocalInfo { codeId: number; @@ -305,48 +308,31 @@ export const useAdminByContractAddresses = ( ); }; -export const useMigrationHistoriesByContractAddressPagination = ( +export const useMigrationHistoriesByContractAddress = ( contractAddress: ContractAddr, offset: number, - pageSize: number -): UseQueryResult[]> => { - const { indexerGraphClient } = useCelatoneApp(); - - const queryFn = useCallback(async () => { - return indexerGraphClient - .request(getMigrationHistoriesByContractAddressPagination, { - contractAddress, - offset, - pageSize, - }) - .then(({ contract_histories }) => - contract_histories.map>( - (history) => ({ - codeId: history.code_id, - sender: history.account.address as Addr, - height: history.block.height, - timestamp: parseDate(history.block.timestamp), - remark: history.remark, - uploader: history.code.account.address as Addr, - cw2Contract: history.code.cw2_contract, - cw2Version: history.code.cw2_version, - }) - ) - ); - }, [contractAddress, offset, pageSize, indexerGraphClient]); + limit: number +) => { + const endpoint = useBaseApiRoute("contracts"); - return useQuery( + return useQuery( [ - CELATONE_QUERY_KEYS.CONTRACT_MIGRATION_HISTORIES_PAGINATION, + CELATONE_QUERY_KEYS.CONTRACT_MIGRATION_HISTORIES_BY_CONTRACT_ADDRESS, + endpoint, contractAddress, + limit, offset, - pageSize, - indexerGraphClient, ], - queryFn, + async () => + getMigrationHistoriesByContractAddress( + endpoint, + contractAddress, + limit, + offset + ), { keepPreviousData: true, - enabled: Boolean(contractAddress), + retry: 1, } ); }; diff --git a/src/lib/services/delegation.ts b/src/lib/services/delegation.ts index 67e2bd453..489657434 100644 --- a/src/lib/services/delegation.ts +++ b/src/lib/services/delegation.ts @@ -132,4 +132,4 @@ export const getDelegationsByAddress = async ( ): Promise => axios .get(`${endpoint}/${encodeURIComponent(address)}/delegations`) - .then((res) => zDelegations.parse(res.data)); + .then(({ data }) => zDelegations.parse(data)); diff --git a/src/lib/services/move/module.ts b/src/lib/services/move/module.ts index 6c749045c..63b1e60e6 100644 --- a/src/lib/services/move/module.ts +++ b/src/lib/services/move/module.ts @@ -50,7 +50,7 @@ export const getModulesByAddress = async ( ): Promise => axios .get(`${endpoint}/${encodeURIComponent(address)}/move/modules`) - .then((res) => zAccountModulesResponse.parse(res.data)); + .then(({ data }) => zAccountModulesResponse.parse(data)); export const getAccountModules = async ( baseEndpoint: string, @@ -195,4 +195,4 @@ export const getModules = async ( offset, }, }) - .then((res) => zModulesResponse.parse(res.data)); + .then(({ data }) => zModulesResponse.parse(data)); diff --git a/src/lib/services/move/pool.ts b/src/lib/services/move/pool.ts index 0a0e8d845..d15992d04 100644 --- a/src/lib/services/move/pool.ts +++ b/src/lib/services/move/pool.ts @@ -43,4 +43,4 @@ const zPairResponse = z export const getMovePoolInfos = async (endpoint: string) => axios .get(`${endpoint}/pools`) - .then((res) => z.array(zPairResponse).parse(res.data)); + .then(({ data }) => z.array(zPairResponse).parse(data)); diff --git a/src/lib/services/move/resource.ts b/src/lib/services/move/resource.ts index 8d5e322f9..08d914c12 100644 --- a/src/lib/services/move/resource.ts +++ b/src/lib/services/move/resource.ts @@ -29,4 +29,4 @@ export const getAccountResources = async ( ): Promise => axios .get(`${endpoint}/${encodeURIComponent(address)}/move/resources`) - .then((res) => zResourcesResponse.parse(res.data)); + .then(({ data }) => zResourcesResponse.parse(data)); diff --git a/src/lib/services/overview.ts b/src/lib/services/overview.ts index 1b0381635..03919207c 100644 --- a/src/lib/services/overview.ts +++ b/src/lib/services/overview.ts @@ -20,4 +20,4 @@ export const getOverviewsStats = async ( ): Promise => axios .get(`${endpoint}/stats`) - .then((res) => zOverviewsStatsResponse.parse(res.data)); + .then(({ data }) => zOverviewsStatsResponse.parse(data)); diff --git a/src/lib/services/proposal.ts b/src/lib/services/proposal.ts index 06301bd95..40bebceba 100644 --- a/src/lib/services/proposal.ts +++ b/src/lib/services/proposal.ts @@ -2,14 +2,13 @@ import type { Coin } from "@cosmjs/stargate"; import axios from "axios"; import { z } from "zod"; -import { - type AccessConfigPermission, - type Addr, - type SnakeToCamelCaseNested, - type Proposal, - type ProposalType, - zAddr, - zUtcDate, +import { zAddr, zUtcDate, zProposalType } from "lib/types"; +import type { + ContractAddr, + AccessConfigPermission, + Addr, + SnakeToCamelCaseNested, + Proposal, } from "lib/types"; import { parseProposalStatus, snakeToCamel } from "lib/utils"; @@ -73,7 +72,7 @@ const zProposalsResponseItem = z resolved_height: z.number().nullish(), status: z.string().transform(parseProposalStatus), title: z.string(), - type: z.string(), + type: zProposalType, voting_end_time: zUtcDate, }) .transform((val) => ({ @@ -84,7 +83,7 @@ const zProposalsResponseItem = z resolvedHeight: val.resolved_height, status: val.status, title: val.title, - type: val.type as ProposalType, // TODO: remove type assertion + type: val.type, votingEndTime: val.voting_end_time, })); @@ -108,4 +107,54 @@ export const getProposalsByAddress = async ( offset, }, }) - .then((res) => zProposalsResponse.parse(res.data)); + .then(({ data }) => zProposalsResponse.parse(data)); + +const zRelatedProposalsResponseItem = z + .object({ + deposit_end_time: zUtcDate, + proposal_id: z.number().nonnegative(), + is_expedited: z.boolean(), + proposer: zAddr, + resolved_height: z.number().nullish(), + status: z.string().transform(parseProposalStatus), + title: z.string(), + type: zProposalType, + voting_end_time: zUtcDate, + }) + .transform((val) => ({ + depositEndTime: val.deposit_end_time, + proposalId: val.proposal_id, + isExpedited: val.is_expedited, + proposer: val.proposer, + resolvedHeight: val.resolved_height, + status: val.status, + title: val.title, + type: val.type, + votingEndTime: val.voting_end_time, + })); + +const zRelatedProposalsResponse = z.object({ + items: z.array(zRelatedProposalsResponseItem), +}); + +export type RelatedProposalsResponse = z.infer< + typeof zRelatedProposalsResponse +>; + +export const getRelatedProposalsByContractAddress = async ( + endpoint: string, + contractAddress: ContractAddr, + limit: number, + offset: number +): Promise => + axios + .get( + `${endpoint}/${encodeURIComponent(contractAddress)}/related-proposals`, + { + params: { + limit, + offset, + }, + } + ) + .then(({ data }) => zRelatedProposalsResponse.parse(data)); diff --git a/src/lib/services/proposalService.ts b/src/lib/services/proposalService.ts index f21cafa3a..296e9de71 100644 --- a/src/lib/services/proposalService.ts +++ b/src/lib/services/proposalService.ts @@ -12,7 +12,6 @@ import { getProposalList, getProposalListCount, getProposalTypes, - getRelatedProposalsByContractAddressPagination, getRelatedProposalsByModuleIdPagination, getRelatedProposalsCountByModuleId, } from "lib/query"; @@ -41,6 +40,7 @@ import { useProposalListExpression } from "./expression"; import type { DepositParamsInternal, ProposalsResponse, + RelatedProposalsResponse, UploadAccess, VotingParamsInternal, } from "./proposal"; @@ -49,49 +49,34 @@ import { fetchGovDepositParams, fetchGovUploadAccessParams, getProposalsByAddress, + getRelatedProposalsByContractAddress, } from "./proposal"; -export const useRelatedProposalsByContractAddressPagination = ( +export const useRelatedProposalsByContractAddress = ( contractAddress: ContractAddr, offset: number, - pageSize: number -): UseQueryResult => { - const { indexerGraphClient } = useCelatoneApp(); - - const queryFn = useCallback(async () => { - return indexerGraphClient - .request(getRelatedProposalsByContractAddressPagination, { - contractAddress, - offset, - pageSize, - }) - .then(({ contract_proposals }) => - contract_proposals.map((proposal) => ({ - proposalId: proposal.proposal_id, - title: proposal.proposal.title, - status: parseProposalStatus(proposal.proposal.status), - votingEndTime: parseDate(proposal.proposal.voting_end_time), - depositEndTime: parseDate(proposal.proposal.deposit_end_time), - resolvedHeight: proposal.resolved_height, - type: proposal.proposal.type as ProposalType, - proposer: proposal.proposal.account?.address as Addr, - isExpedited: Boolean(proposal.proposal.is_expedited), - })) - ); - }, [contractAddress, offset, pageSize, indexerGraphClient]); + limit: number +) => { + const endpoint = useBaseApiRoute("contracts"); - return useQuery( + return useQuery( [ - CELATONE_QUERY_KEYS.RELATED_PROPOSALS_BY_CONTRACT_ADDRESS_PAGINATION, + CELATONE_QUERY_KEYS.RELATED_PROPOSALS_BY_CONTRACT_ADDRESS, + endpoint, contractAddress, + limit, offset, - pageSize, - indexerGraphClient, ], - queryFn, + async () => + getRelatedProposalsByContractAddress( + endpoint, + contractAddress, + limit, + offset + ), { + retry: 1, keepPreviousData: true, - enabled: !!contractAddress, } ); }; diff --git a/src/lib/services/tx.ts b/src/lib/services/tx.ts index ab4af4f96..b8fa5d7ba 100644 --- a/src/lib/services/tx.ts +++ b/src/lib/services/tx.ts @@ -179,7 +179,7 @@ export const getTxs = async ( is_initia: isInitia, }, }) - .then((res) => zTxsResponse.parse(res.data)); + .then(({ data }) => zTxsResponse.parse(data)); const zAccountTxsResponseItem = zBaseTxsResponseItem .extend({ @@ -259,7 +259,7 @@ export const getTxsByAddress = async ( ...(search !== undefined && { search }), }, }) - .then((res) => zAccountTxsResponse.parse(res.data)); + .then(({ data }) => zAccountTxsResponse.parse(data)); }; const zBlockTxsResponse = z.object({ @@ -287,7 +287,7 @@ export const getTxsByBlockHeight = async ( is_initia: isInitia, }, }) - .then((res) => zBlockTxsResponse.parse(res.data)); + .then(({ data }) => zBlockTxsResponse.parse(data)); const zModuleTxsResponse = z.object({ items: z.array(zTxsResponseItem), @@ -317,7 +317,7 @@ export const getTxsByModule = async ( }, } ) - .then((res) => zModuleTxsResponse.parse(res.data)); + .then(({ data }) => zModuleTxsResponse.parse(data)); const zTxsCountResponse = z .object({ @@ -344,5 +344,5 @@ export const getAPITxsCountByAddress = async ( ...(search !== undefined && { search }), }, }) - .then((res) => zTxsCountResponse.parse(res.data)); + .then(({ data }) => zTxsCountResponse.parse(data)); }; diff --git a/src/lib/types/contract.ts b/src/lib/types/contract.ts index 249be4e80..29ff4516b 100644 --- a/src/lib/types/contract.ts +++ b/src/lib/types/contract.ts @@ -1,5 +1,10 @@ +import { z } from "zod"; + import type { ContractLocalInfo } from "lib/stores/contract"; -import type { Addr, Nullable, Option, RemarkType } from "lib/types"; + +import type { Addr } from "./addrs"; +import type { Nullable, Option } from "./common"; +import { zRemarkType } from "./tx"; export enum RemarkOperation { CONTRACT_CODE_HISTORY_OPERATION_TYPE_INIT = "CONTRACT_CODE_HISTORY_OPERATION_TYPE_INIT", @@ -7,11 +12,13 @@ export enum RemarkOperation { CONTRACT_CODE_HISTORY_OPERATION_TYPE_GENESIS = "CONTRACT_CODE_HISTORY_OPERATION_TYPE_GENESIS", } -export interface ContractHistoryRemark { - operation: RemarkOperation; - type: RemarkType; - value: string | number; -} +export const zContractHistoryRemark = z.object({ + operation: z.nativeEnum(RemarkOperation), + type: zRemarkType, + value: z.union([z.string(), z.number()]).optional(), +}); + +export type ContractHistoryRemark = z.infer; export interface ContractInfo extends ContractLocalInfo { admin: Option; @@ -28,6 +35,6 @@ export interface ContractMigrationHistory { timestamp: Date; remark: ContractHistoryRemark; uploader: Addr; - cw2Contract: Option>; - cw2Version: Option>; + cw2Contract: Nullable; + cw2Version: Nullable; } diff --git a/src/lib/types/proposal.ts b/src/lib/types/proposal.ts index 24aaf3c89..5e6db17fc 100644 --- a/src/lib/types/proposal.ts +++ b/src/lib/types/proposal.ts @@ -1,3 +1,5 @@ +import { z } from "zod"; + import type { Addr, Nullable, Option } from "lib/types"; export enum ProposalStatus { @@ -44,10 +46,12 @@ enum ProposalTypeOsmosis { SET_PROTOREV_ADMIN_ACCOUNT = "SetProtoRevAdminAccount", } -export type ProposalType = - | ProposalTypeCosmos - | ProposalTypeCosmWasm - | ProposalTypeOsmosis; +export const zProposalType = z.union([ + z.nativeEnum(ProposalTypeCosmos), + z.nativeEnum(ProposalTypeCosmWasm), + z.nativeEnum(ProposalTypeOsmosis), +]); +export type ProposalType = z.infer; export interface Proposal { proposalId: number; diff --git a/src/lib/types/tx/transaction.ts b/src/lib/types/tx/transaction.ts index 0507c8ad0..256fdb0e3 100644 --- a/src/lib/types/tx/transaction.ts +++ b/src/lib/types/tx/transaction.ts @@ -1,4 +1,5 @@ import type { Log } from "@cosmjs/stargate/build/logs"; +import { z } from "zod"; import type { Addr, Option } from "lib/types"; @@ -81,4 +82,5 @@ export type PoolTxFilter = | "is_collect" | "is_migrate"; -export type RemarkType = "genesis" | "governance" | "transaction"; +export const zRemarkType = z.enum(["genesis", "governance", "transaction"]); +export type RemarkType = z.infer;