diff --git a/CHANGELOG.md b/CHANGELOG.md index 6117dad48..37ff7ed58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#961](https://github.com/alleslabs/celatone-frontend/pull/961) Add and refactor proposal related lcd endpoints - [#952](https://github.com/alleslabs/celatone-frontend/pull/952) Support module details page lite version with LCD endpoint - [#940](https://github.com/alleslabs/celatone-frontend/pull/940) Support my published modules page lite version with LCD endpoint - [#956](https://github.com/alleslabs/celatone-frontend/pull/956) Add landlord-1 network diff --git a/src/lib/app-provider/env.ts b/src/lib/app-provider/env.ts index dfa4e5759..c5610d4c1 100644 --- a/src/lib/app-provider/env.ts +++ b/src/lib/app-provider/env.ts @@ -82,8 +82,7 @@ export enum CELATONE_QUERY_KEYS { PROPOSALS_LCD = "CELATONE_QUERY_PROPOSALS_LCD", PROPOSAL_PARAMS = "CELATONE_QUERY_PROPOSAL_PARAMS", PROPOSAL_TYPES = "CELATONE_QUERY_PROPOSAL_TYPES", - GOV_PARAMS = "CELATONE_QUERY_GOV_PARAMS", - UPLOAD_ACCESS_PARAMS = "CELATONE_QUERY_UPLOAD_ACCESS_PARAMS", + UPLOAD_ACCESS_PARAMS_LCD = "CELATONE_QUERY_UPLOAD_ACCESS_PARAMS_LCD", // PUBLIC PROJECT PUBLIC_PROJECTS = "CELATONE_QUERY_PUBLIC_PROJECTS", PUBLIC_PROJECT_BY_SLUG = "CELATONE_QUERY_PUBLIC_PROJECT_BY_SLUG", diff --git a/src/lib/model/proposal.ts b/src/lib/model/proposal.ts new file mode 100644 index 000000000..3da16ccf8 --- /dev/null +++ b/src/lib/model/proposal.ts @@ -0,0 +1,143 @@ +import { useAssetInfos } from "lib/services/assetService"; +import { useMovePoolInfos } from "lib/services/move/poolService"; +import { useProposalParams } from "lib/services/proposal"; +import { big } from "lib/types"; +import type { Coin, Option, ProposalParams, Token, U } from "lib/types"; +import { + coinToTokenWithValue, + compareTokenWithValues, + deexponentify, + formatTokenWithValue, + getTokenLabel, +} from "lib/utils"; + +// TODO: remove and use `useDerivedProposalParams` instead +export interface GovParams { + depositParams: { + minDeposit: { + amount: U>; + denom: string; + formattedAmount: Token; + formattedDenom: string; + formattedToken: string; + precision: number; + }; + minInitialDeposit: Token; + maxDepositPeriod: string; + minExpeditedDeposit: Option; + minInitialDepositRatio: Option; + }; + votingParams: { + votingPeriod: string; + expeditedVotingPeriod: Option; + }; +} + +export const useGovParamsDeprecated = (): { + data: Option; + isLoading: boolean; +} => { + const { data: assetInfos } = useAssetInfos({ withPrices: false }); + const { data: movePoolInfos } = useMovePoolInfos({ withPrices: false }); + + const { data, isLoading } = useProposalParams(); + + if (!data) return { data: undefined, isLoading }; + + const minDepositParam = data.minDeposit[0]; + const minDepositToken = coinToTokenWithValue( + minDepositParam.denom, + minDepositParam.amount, + assetInfos, + movePoolInfos + ); + const minDepositAmount = deexponentify( + minDepositToken.amount, + minDepositToken.precision + ).toFixed() as Token; + return { + data: { + depositParams: { + minDeposit: { + ...minDepositParam, + amount: minDepositToken.amount, + formattedAmount: minDepositAmount, + formattedDenom: getTokenLabel( + minDepositToken.denom, + minDepositToken.symbol + ), + formattedToken: formatTokenWithValue(minDepositToken), + precision: minDepositToken.precision ?? 0, + }, + minInitialDeposit: big(data.minInitialDepositRatio) + .times(minDepositAmount) + .toFixed(2) as Token, + maxDepositPeriod: data.maxDepositPeriod, + minInitialDepositRatio: data.minInitialDepositRatio.toString(), + minExpeditedDeposit: data.expeditedMinDeposit, + }, + votingParams: { + votingPeriod: data.votingPeriod, + expeditedVotingPeriod: data.expeditedVotingPeriod, + }, + }, + isLoading, + }; +}; + +export const useDerivedProposalParams = ( + withPrices = false +): { + data: Option; + isLoading: boolean; +} => { + const { data, isLoading } = useProposalParams(); + const { data: assetInfos, isLoading: isAssetInfosLoading } = useAssetInfos({ + withPrices, + }); + const { data: movePoolInfos, isLoading: isMovePoolInfosLoading } = + useMovePoolInfos({ withPrices }); + + if (isLoading || isAssetInfosLoading || isMovePoolInfosLoading || !data) + return { + data: undefined, + isLoading: isLoading || isAssetInfosLoading || isMovePoolInfosLoading, + }; + + return { + data: { + ...data, + minDeposit: data.minDeposit + .map((coin) => + coinToTokenWithValue( + coin.denom, + coin.amount, + assetInfos, + movePoolInfos + ) + ) + .sort(compareTokenWithValues), + expeditedMinDeposit: data.expeditedMinDeposit + ?.map((coin) => + coinToTokenWithValue( + coin.denom, + coin.amount, + assetInfos, + movePoolInfos + ) + ) + .sort(compareTokenWithValues), + emergencyMinDeposit: data.emergencyMinDeposit + ?.map((coin) => + coinToTokenWithValue( + coin.denom, + coin.amount, + assetInfos, + movePoolInfos + ) + ) + .sort(compareTokenWithValues), + }, + isLoading: false, + }; +}; diff --git a/src/lib/pages/deploy/index.tsx b/src/lib/pages/deploy/index.tsx index 9f1671666..2bf17a577 100644 --- a/src/lib/pages/deploy/index.tsx +++ b/src/lib/pages/deploy/index.tsx @@ -17,7 +17,7 @@ import { Loading } from "lib/components/Loading"; import { Stepper } from "lib/components/stepper"; import { UserDocsLink } from "lib/components/UserDocsLink"; import WasmPageContainer from "lib/components/WasmPageContainer"; -import { useUploadAccessParams } from "lib/services/wasm/code"; +import { useUploadAccessParamsLcd } from "lib/services/wasm/code"; import { AccessConfigPermission } from "lib/types"; const getAlertContent = ( @@ -59,7 +59,7 @@ const Deploy = () => { const { chainConfig: { prettyName: chainPrettyName }, } = useCelatoneApp(); - const { data, isFetching } = useUploadAccessParams(); + const { data, isFetching } = useUploadAccessParamsLcd(); const isPermissionedNetwork = data?.permission !== AccessConfigPermission.EVERYBODY; diff --git a/src/lib/pages/migrate/index.tsx b/src/lib/pages/migrate/index.tsx index 35d5f3a92..414cc217f 100644 --- a/src/lib/pages/migrate/index.tsx +++ b/src/lib/pages/migrate/index.tsx @@ -17,7 +17,7 @@ import { Loading } from "lib/components/Loading"; import { Stepper } from "lib/components/stepper"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useUploadCode } from "lib/hooks"; -import { useUploadAccessParams } from "lib/services/wasm/code"; +import { useUploadAccessParamsLcd } from "lib/services/wasm/code"; import { useContractLcd } from "lib/services/wasm/contract"; import type { BechAddr32 } from "lib/types"; import { getFirstQueryParam } from "lib/utils"; @@ -38,7 +38,7 @@ const Migrate = () => { useWasmConfig({ shouldRedirect: true }); const router = useRouter(); const navigate = useInternalNavigate(); - const { data: uploadAccessParams, isFetching } = useUploadAccessParams(); + const { data: uploadAccessParams, isFetching } = useUploadAccessParamsLcd(); const { proceed, formData, diff --git a/src/lib/pages/proposal-details/components/InvalidProposal.tsx b/src/lib/pages/proposal-details/components/InvalidProposal.tsx new file mode 100644 index 000000000..91cfa4a4e --- /dev/null +++ b/src/lib/pages/proposal-details/components/InvalidProposal.tsx @@ -0,0 +1,5 @@ +import { InvalidState } from "lib/components/state"; + +export const InvalidProposal = () => ( + +); diff --git a/src/lib/pages/proposal-details/components/index.ts b/src/lib/pages/proposal-details/components/index.ts index d49ecfafc..46cbdbd0a 100644 --- a/src/lib/pages/proposal-details/components/index.ts +++ b/src/lib/pages/proposal-details/components/index.ts @@ -1,3 +1,4 @@ export * from "./proposal-overview"; export * from "./proposal-top"; export * from "./vote-details"; +export * from "./InvalidProposal"; diff --git a/src/lib/pages/proposal-details/components/proposal-overview/proposal-period-overview/VotingOverview.tsx b/src/lib/pages/proposal-details/components/proposal-overview/proposal-period-overview/VotingOverview.tsx index b595af940..5bb4fd578 100644 --- a/src/lib/pages/proposal-details/components/proposal-overview/proposal-period-overview/VotingOverview.tsx +++ b/src/lib/pages/proposal-details/components/proposal-overview/proposal-period-overview/VotingOverview.tsx @@ -1,6 +1,6 @@ import { Button, Flex, Text } from "@chakra-ui/react"; -import type { ProposalOverviewProps } from "../.."; +import type { ProposalOverviewProps } from ".."; import { ErrorFetchingProposalInfos } from "../../ErrorFetchingProposalInfos"; import { useInternalNavigate } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; diff --git a/src/lib/pages/proposal-details/components/proposal-top/index.tsx b/src/lib/pages/proposal-details/components/proposal-top/index.tsx index e9891aacf..2457da63d 100644 --- a/src/lib/pages/proposal-details/components/proposal-top/index.tsx +++ b/src/lib/pages/proposal-details/components/proposal-top/index.tsx @@ -135,11 +135,9 @@ export const ProposalTop = ({ proposalData }: ProposalTopProps) => { {!isMobile && } )} - {proposalData.createdTimestamp && ( - - {formatUTC(proposalData.createdTimestamp)} - - )} + + {formatUTC(proposalData.submitTime)} + diff --git a/src/lib/pages/proposal-details/data.ts b/src/lib/pages/proposal-details/data.ts index 0acc2e54f..2c3172145 100644 --- a/src/lib/pages/proposal-details/data.ts +++ b/src/lib/pages/proposal-details/data.ts @@ -1,65 +1,8 @@ -import { useMobile } from "lib/app-provider"; import { useAssetInfos } from "lib/services/assetService"; import { useMovePoolInfos } from "lib/services/move/poolService"; -import { useProposalData, useProposalParams } from "lib/services/proposal"; -import type { Nullable, Option, ProposalData, ProposalParams } from "lib/types"; -import { coinToTokenWithValue, compareTokenWithValues } from "lib/utils"; - -export const useDerivedProposalParams = (): { - data: Option; - isLoading: boolean; -} => { - const isMobile = useMobile(); - const { data, isLoading } = useProposalParams(); - const { data: assetInfos, isLoading: isAssetInfosLoading } = useAssetInfos({ - withPrices: !isMobile, - }); - const { data: movePoolInfos, isLoading: isMovePoolInfosLoading } = - useMovePoolInfos({ withPrices: !isMobile }); - - if (isLoading || isAssetInfosLoading || isMovePoolInfosLoading || !data) - return { - data: undefined, - isLoading: isLoading || isAssetInfosLoading || isMovePoolInfosLoading, - }; - - return { - data: { - ...data, - minDeposit: data.minDeposit - .map((coin) => - coinToTokenWithValue( - coin.denom, - coin.amount, - assetInfos, - movePoolInfos - ) - ) - .sort(compareTokenWithValues), - expeditedMinDeposit: data.expeditedMinDeposit - ?.map((coin) => - coinToTokenWithValue( - coin.denom, - coin.amount, - assetInfos, - movePoolInfos - ) - ) - .sort(compareTokenWithValues), - emergencyMinDeposit: data.emergencyMinDeposit - ?.map((coin) => - coinToTokenWithValue( - coin.denom, - coin.amount, - assetInfos, - movePoolInfos - ) - ) - .sort(compareTokenWithValues), - }, - isLoading: false, - }; -}; +import { useProposalData } from "lib/services/proposal"; +import type { Nullable, Option, ProposalData } from "lib/types"; +import { coinToTokenWithValue } from "lib/utils"; interface DerivedProposalDataResponse { data: Option<{ diff --git a/src/lib/pages/proposal-details/index.tsx b/src/lib/pages/proposal-details/index.tsx index 545aa0ef2..e2c7309f9 100644 --- a/src/lib/pages/proposal-details/index.tsx +++ b/src/lib/pages/proposal-details/index.tsx @@ -3,33 +3,38 @@ import { useRouter } from "next/router"; import { useCallback, useEffect } from "react"; import { AmpEvent, track, trackUseTab } from "lib/amplitude"; -import { useGovConfig, useInternalNavigate } from "lib/app-provider"; +import { useGovConfig, useInternalNavigate, useMobile } from "lib/app-provider"; import { CustomTab } from "lib/components/CustomTab"; import { Loading } from "lib/components/Loading"; import PageContainer from "lib/components/PageContainer"; -import { ErrorFetching, InvalidState } from "lib/components/state"; +import { ErrorFetching } from "lib/components/state"; import { UserDocsLink } from "lib/components/UserDocsLink"; +import { useDerivedProposalParams } from "lib/model/proposal"; import { useProposalVotesInfo } from "lib/services/proposal"; -import { ProposalOverview, ProposalTop, VoteDetails } from "./components"; -import { useDerivedProposalData, useDerivedProposalParams } from "./data"; +import { + InvalidProposal, + ProposalOverview, + ProposalTop, + VoteDetails, +} from "./components"; +import { useDerivedProposalData } from "./data"; import type { ProposalDetailsQueryParams } from "./types"; import { TabIndex, zProposalDetailsQueryParams } from "./types"; -const InvalidProposal = () => ; - const ProposalDetailsBody = ({ proposalId, tab, }: ProposalDetailsQueryParams) => { useGovConfig({ shouldRedirect: true }); + const isMobile = useMobile(); const navigate = useInternalNavigate(); const { data, isLoading } = useDerivedProposalData(proposalId); const { data: votesInfo, isLoading: isVotesInfoLoading } = useProposalVotesInfo(proposalId); const { data: params, isLoading: isParamsLoading } = - useDerivedProposalParams(); + useDerivedProposalParams(!isMobile); const handleTabChange = useCallback( (nextTab: TabIndex) => () => { diff --git a/src/lib/pages/proposal/components/InitialDeposit.tsx b/src/lib/pages/proposal/components/InitialDeposit.tsx index 60934001a..390065afd 100644 --- a/src/lib/pages/proposal/components/InitialDeposit.tsx +++ b/src/lib/pages/proposal/components/InitialDeposit.tsx @@ -1,6 +1,6 @@ import { Box, Heading, Text } from "@chakra-ui/react"; -import type { GovParams } from "lib/services/types"; +import type { GovParams } from "lib/model/proposal"; import type { Option } from "lib/types"; import { formatSeconds } from "lib/utils"; diff --git a/src/lib/pages/proposal/store-code/index.tsx b/src/lib/pages/proposal/store-code/index.tsx index ccf319f38..1c500169a 100644 --- a/src/lib/pages/proposal/store-code/index.tsx +++ b/src/lib/pages/proposal/store-code/index.tsx @@ -54,7 +54,8 @@ import { InstantiatePermissionRadio } from "lib/components/upload/InstantiatePer import { SimulateMessageRender } from "lib/components/upload/SimulateMessageRender"; import { UploadCard } from "lib/components/upload/UploadCard"; import { useGetMaxLengthError, useTxBroadcast } from "lib/hooks"; -import { useGovParams } from "lib/services/proposal"; +import { useGovParamsDeprecated } from "lib/model/proposal"; +import { useUploadAccessParamsLcd } from "lib/services/wasm/code"; import type { BechAddr, SimulateStatus, UploadSectionState } from "lib/types"; import { AccessConfigPermission, AccessType } from "lib/types"; import { @@ -95,10 +96,11 @@ const StoreCodeProposal = () => { const getMaxLengthError = useGetMaxLengthError(); const { address: walletAddress } = useCurrentChain(); const fabricateFee = useFabricateFee(); - const { data: govParams } = useGovParams(); + const { data: govParams } = useGovParamsDeprecated(); + const { data: uploadAccessParams } = useUploadAccessParamsLcd(); const minDeposit = govParams?.depositParams.minDeposit; const isPermissionless = - govParams?.uploadAccess.permission === AccessConfigPermission.EVERYBODY; + uploadAccessParams?.permission === AccessConfigPermission.EVERYBODY; const { validateUserAddress, validateContractAddress } = useValidateAddress(); const submitStoreCodeProposalTx = useSubmitStoreCodeProposalTx(); const { broadcast } = useTxBroadcast(); diff --git a/src/lib/pages/proposal/whitelist/index.tsx b/src/lib/pages/proposal/whitelist/index.tsx index 9641f2799..d8572a9b1 100644 --- a/src/lib/pages/proposal/whitelist/index.tsx +++ b/src/lib/pages/proposal/whitelist/index.tsx @@ -43,7 +43,8 @@ import { CustomIcon } from "lib/components/icon"; import PageContainer from "lib/components/PageContainer"; import { StickySidebar } from "lib/components/StickySidebar"; import { useGetMaxLengthError, useTxBroadcast } from "lib/hooks"; -import { useGovParams } from "lib/services/proposal"; +import { useGovParamsDeprecated } from "lib/model/proposal"; +import { useUploadAccessParamsLcd } from "lib/services/wasm/code"; import type { BechAddr } from "lib/types"; import { AccessConfigPermission } from "lib/types"; import { composeSubmitWhitelistProposalMsg, getAmountToVote } from "lib/utils"; @@ -72,7 +73,8 @@ const ProposalToWhitelist = () => { chainConfig: { prettyName }, } = useCelatoneApp(); const fabricateFee = useFabricateFee(); - const { data: govParams } = useGovParams(); + const { data: govParams } = useGovParamsDeprecated(); + const { data: uploadAccessParams } = useUploadAccessParamsLcd(); const submitProposalTx = useSubmitWhitelistProposalTx(); const { broadcast } = useTxBroadcast(); const { @@ -98,7 +100,7 @@ const ProposalToWhitelist = () => { const minDeposit = govParams?.depositParams.minDeposit; const isPermissionless = - govParams?.uploadAccess.permission === AccessConfigPermission.EVERYBODY; + uploadAccessParams?.permission === AccessConfigPermission.EVERYBODY; const addressesArray = addresses.map((addressObj) => addressObj.address); const formErrorsKey = Object.keys(formErrors); const enabledTx = useMemo( @@ -128,11 +130,11 @@ const ProposalToWhitelist = () => { title, description, changesValue: JSON.stringify({ - ...govParams?.uploadAccess, + ...uploadAccessParams, permission: !isPermissionless ? AccessConfigPermission.ANY_OF_ADDRESSES : AccessConfigPermission.EVERYBODY, - addresses: govParams?.uploadAccess.addresses?.concat(addressesArray), + addresses: uploadAccessParams?.addresses?.concat(addressesArray), }), initialDeposit, proposer: walletAddress, @@ -142,12 +144,12 @@ const ProposalToWhitelist = () => { }, [ addressesArray, description, - govParams?.uploadAccess, initialDeposit, - minDeposit, + isPermissionless, + minDeposit?.precision, title, + uploadAccessParams, walletAddress, - isPermissionless, ]); const { isFetching: isSimulating } = useSimulateFeeQuery({ @@ -335,7 +337,7 @@ const ProposalToWhitelist = () => { addressObj.address === addresses[idx].address ) && "You already input this address", whitelisted: () => - govParams?.uploadAccess.addresses?.includes( + uploadAccessParams?.addresses?.includes( addresses[idx].address ) ? "This address is already included in whitelist" diff --git a/src/lib/pages/proposals/components/ProposalsTableLite.tsx b/src/lib/pages/proposals/components/ProposalsTableLite.tsx index d5605a2ac..8ab221df3 100644 --- a/src/lib/pages/proposals/components/ProposalsTableLite.tsx +++ b/src/lib/pages/proposals/components/ProposalsTableLite.tsx @@ -8,6 +8,7 @@ import { ProposalsTable } from "lib/components/table"; import { useDebounce } from "lib/hooks"; import { useProposalDataLcd, useProposalsLcd } from "lib/services/proposal"; import type { ProposalStatus } from "lib/types"; +import { isPositiveInt } from "lib/utils"; import { ProposalStatusFilter } from "./ProposalStatusFilter"; @@ -25,8 +26,8 @@ export const ProposalsTableLite = () => { isFetchingNextPage, } = useProposalsLcd(status[0]); - const { data: proposalData, isLoading: isProposalDataLoading } = - useProposalDataLcd(debouncedSearch); + const { data: proposalData, isFetching: isProposalDataFetching } = + useProposalDataLcd(debouncedSearch, isPositiveInt(debouncedSearch)); const proposal = proposalData ? [proposalData] : []; const proposals = @@ -35,7 +36,7 @@ export const ProposalsTableLite = () => { const isLoadNext = hasNextPage && !isProposalsLoading && - !isProposalDataLoading && + !isProposalDataFetching && proposals && proposals.length > 1; @@ -68,7 +69,7 @@ export const ProposalsTableLite = () => { diff --git a/src/lib/pages/stored-codes/index.tsx b/src/lib/pages/stored-codes/index.tsx index a0b361f3d..6198b99e8 100644 --- a/src/lib/pages/stored-codes/index.tsx +++ b/src/lib/pages/stored-codes/index.tsx @@ -18,7 +18,7 @@ import { MyStoredCodesTable } from "lib/components/table"; import { UserDocsLink } from "lib/components/UserDocsLink"; import type { PermissionFilterValue } from "lib/hooks"; import { useMyCodesData } from "lib/model/code"; -import { useUploadAccessParams } from "lib/services/wasm/code"; +import { useUploadAccessParamsLcd } from "lib/services/wasm/code"; import { AccessConfigPermission } from "lib/types"; import { ProposalButton } from "./components/ProposalButton"; @@ -49,7 +49,8 @@ const StoredCodes = observer(() => { storedCodes: stored, isStoredCodesLoading, } = useMyCodesData(keyword, permissionValue); - const { data, isFetching: isUploadAccessFetching } = useUploadAccessParams(); + const { data, isFetching: isUploadAccessFetching } = + useUploadAccessParamsLcd(); const isPermissionedNetwork = data?.permission !== AccessConfigPermission.EVERYBODY; diff --git a/src/lib/pages/upload/upload.tsx b/src/lib/pages/upload/upload.tsx index e174e03f3..765958203 100644 --- a/src/lib/pages/upload/upload.tsx +++ b/src/lib/pages/upload/upload.tsx @@ -13,7 +13,7 @@ import { UploadSection } from "lib/components/upload/UploadSection"; import { UserDocsLink } from "lib/components/UserDocsLink"; import WasmPageContainer from "lib/components/WasmPageContainer"; import { useUploadCode } from "lib/hooks"; -import { useUploadAccessParams } from "lib/services/wasm/code"; +import { useUploadAccessParamsLcd } from "lib/services/wasm/code"; import { AccessConfigPermission } from "lib/types"; export const Upload = ({ @@ -24,7 +24,7 @@ export const Upload = ({ const router = useRouter(); const { address } = useCurrentChain(); const navigate = useInternalNavigate(); - const { data, isLoading } = useUploadAccessParams(); + const { data, isLoading } = useUploadAccessParamsLcd(); const { proceed, formData, diff --git a/src/lib/services/proposal/index.ts b/src/lib/services/proposal/index.ts index d4d84aea0..b7142decb 100644 --- a/src/lib/services/proposal/index.ts +++ b/src/lib/services/proposal/index.ts @@ -1,27 +1,22 @@ -import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; import type { UseQueryOptions, UseQueryResult } from "@tanstack/react-query"; -import { useCallback } from "react"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; -import { useAssetInfos } from "../assetService"; -import { useMovePoolInfos } from "../move/poolService"; import type { - GovParams, ProposalAnswerCountsResponse, ProposalDataResponse, + ProposalDataResponseLcd, ProposalsResponse, - ProposalsResponseItemLcd, ProposalsResponseLcd, ProposalValidatorVotesResponse, ProposalVotesResponse, RelatedProposalsResponse, } from "../types/proposal"; -import { getUploadAccessParamsLcd } from "../wasm/code"; import { CELATONE_QUERY_KEYS, useBaseApiRoute, useLcdEndpoint, + useTierConfig, } from "lib/app-provider"; -import { big } from "lib/types"; import type { BechAddr, BechAddr20, @@ -33,14 +28,7 @@ import type { ProposalType, ProposalVotesInfo, ProposalVoteType, - Token, } from "lib/types"; -import { - coinToTokenWithValue, - deexponentify, - formatTokenWithValue, - getTokenLabel, -} from "lib/utils"; import { getProposalAnswerCounts, @@ -55,76 +43,24 @@ import { getRelatedProposalsByContractAddress, } from "./api"; import { - getDepositParamsLcd, getProposalDataLcd, + getProposalParamsLcd, getProposalsLcd, - getVotingParamsLcd, } from "./lcd"; -export const useGovParams = (): UseQueryResult => { +export const useProposalParams = () => { + const tier = useTierConfig(); + const apiEndpoint = useBaseApiRoute("proposals"); const lcdEndpoint = useLcdEndpoint(); - const { data: assetInfos } = useAssetInfos({ withPrices: false }); - const { data: movePoolInfos } = useMovePoolInfos({ withPrices: false }); - const queryFn = useCallback( - () => - Promise.all([ - getDepositParamsLcd(lcdEndpoint), - getUploadAccessParamsLcd(lcdEndpoint), - getVotingParamsLcd(lcdEndpoint), - ]).then((params) => { - const minDepositParam = params[0].minDeposit[0]; - const minDepositToken = coinToTokenWithValue( - minDepositParam.denom, - minDepositParam.amount, - assetInfos, - movePoolInfos - ); - const minDepositAmount = deexponentify( - minDepositToken.amount, - minDepositToken.precision - ).toFixed() as Token; - - return { - depositParams: { - ...params[0], - minDeposit: { - ...minDepositParam, - amount: minDepositToken.amount, - formattedAmount: minDepositAmount, - formattedDenom: getTokenLabel( - minDepositToken.denom, - minDepositToken.symbol - ), - formattedToken: formatTokenWithValue(minDepositToken), - precision: minDepositToken.precision ?? 0, - }, - minInitialDeposit: big(params[0].minInitialDepositRatio) - .times(minDepositAmount) - .toFixed(2) as Token, - }, - uploadAccess: params[1], - votingParams: params[2], - }; - }), - [assetInfos, lcdEndpoint, movePoolInfos] - ); - - return useQuery( - [CELATONE_QUERY_KEYS.GOV_PARAMS, lcdEndpoint, assetInfos], - queryFn, - { - keepPreviousData: true, - refetchOnWindowFocus: false, - } - ); -}; + const [endpoint, queryFn] = + tier === "full" + ? [apiEndpoint, getProposalParams] + : [lcdEndpoint, getProposalParamsLcd]; -export const useProposalParams = () => { - const endpoint = useBaseApiRoute("proposals"); return useQuery>( [CELATONE_QUERY_KEYS.PROPOSAL_PARAMS, endpoint], - async () => getProposalParams(endpoint), + async () => queryFn(endpoint), { retry: 1, refetchOnWindowFocus: false } ); }; @@ -258,7 +194,7 @@ export const useProposalData = (id: number, enabled = true) => { export const useProposalDataLcd = (id: string, enabled = true) => { const lcdEndpoint = useLcdEndpoint(); - return useQuery( + return useQuery( [CELATONE_QUERY_KEYS.PROPOSAL_DATA_LCD, lcdEndpoint, id], async () => getProposalDataLcd(lcdEndpoint, id), { diff --git a/src/lib/services/proposal/lcd.ts b/src/lib/services/proposal/lcd.ts index 25a631c0b..ee31857fd 100644 --- a/src/lib/services/proposal/lcd.ts +++ b/src/lib/services/proposal/lcd.ts @@ -1,27 +1,21 @@ import axios from "axios"; import { - zDepositParamsLcd, - zProposalsResponseItemLcd, + zProposalDataResponseLcd, + zProposalParamsResponseLcd, zProposalsResponseLcd, - zVotingParamsLcd, } from "lib/services/types"; import type { - ProposalsResponseItemLcd, + ProposalDataResponseLcd, ProposalsResponseLcd, } from "lib/services/types"; import type { Option, ProposalStatus } from "lib/types"; import { parseWithError } from "lib/utils"; -export const getDepositParamsLcd = (lcdEndpoint: string) => +export const getProposalParamsLcd = (lcdEndpoint: string) => axios - .get(`${lcdEndpoint}/cosmos/gov/v1beta1/params/deposit`) - .then(({ data }) => parseWithError(zDepositParamsLcd, data)); - -export const getVotingParamsLcd = (lcdEndpoint: string) => - axios - .get(`${lcdEndpoint}/cosmos/gov/v1beta1/params/voting`) - .then(({ data }) => parseWithError(zVotingParamsLcd, data)); + .get(`${lcdEndpoint}/cosmos/gov/v1/params/deposit`) + .then(({ data }) => parseWithError(zProposalParamsResponseLcd, data).param); export const getProposalsLcd = async ( endpoint: string, @@ -44,9 +38,9 @@ export const getProposalsLcd = async ( export const getProposalDataLcd = async ( endpoint: string, id: string -): Promise => +): Promise => axios .get(`${endpoint}/cosmos/gov/v1/proposals/${encodeURI(id)}`) .then(({ data }) => - parseWithError(zProposalsResponseItemLcd, data.proposal) + parseWithError(zProposalDataResponseLcd, data.proposal) ); diff --git a/src/lib/services/types/proposal.ts b/src/lib/services/types/proposal.ts index dd613eb98..ba04e7bec 100644 --- a/src/lib/services/types/proposal.ts +++ b/src/lib/services/types/proposal.ts @@ -21,58 +21,10 @@ import type { ProposalValidatorVote, ProposalVote, ProposalVotesInfo, - Token, - U, } from "lib/types"; import { zPagination } from "lib/types/rest"; import { parseTxHash, snakeToCamel } from "lib/utils"; -import type { UploadAccessParams } from "./wasm"; - -export interface MinDeposit { - amount: U>; - denom: string; - formattedAmount: Token; - formattedDenom: string; - formattedToken: string; - precision: number; -} - -export const zDepositParamsLcd = z - .object({ - min_deposit: zCoin.array(), - max_deposit_period: z.string(), - min_expedited_deposit: zCoin.array(), - min_initial_deposit_ratio: z.string(), - }) - .transform(snakeToCamel); -type DepositParamsLcd = z.infer; - -export interface DepositParams extends Omit { - minDeposit: MinDeposit; - minInitialDeposit: Token; -} - -export const zVotingParamsLcd = z - .object({ - voting_period: z.string(), - proposal_voting_periods: z - .object({ - proposal_type: z.string(), - voting_period: z.string(), - }) - .array(), - expedited_voting_period: z.string(), - }) - .transform(snakeToCamel); -type VotingParamsLcd = z.infer; - -export interface GovParams { - depositParams: DepositParams; - uploadAccess: UploadAccessParams; - votingParams: VotingParamsLcd; -} - export const zProposalParamsResponse = z .object({ min_deposit: zCoin.array(), @@ -93,6 +45,10 @@ export const zProposalParamsResponse = z }) .transform>(snakeToCamel); +export const zProposalParamsResponseLcd = z.object({ + param: zProposalParamsResponse, +}); + export const zProposal = z.object({ deposit_end_time: zUtcDate, id: z.number().nonnegative(), @@ -223,7 +179,7 @@ export type ProposalAnswerCountsResponse = z.infer< typeof zProposalAnswerCountsResponse >; -export const zProposalsResponseItemLcd = z +export const zProposalDataResponseLcd = z .object({ id: z.coerce.number(), messages: z.array(zMessageResponse).nullable(), @@ -245,7 +201,7 @@ export const zProposalsResponseItemLcd = z proposer: zBechAddr, expedited: z.boolean().optional().default(false), }) - .transform((val) => ({ + .transform>((val) => ({ ...snakeToCamel(val), status: val.status .replace("PROPOSAL_STATUS_", "") @@ -255,13 +211,20 @@ export const zProposalsResponseItemLcd = z resolvedHeight: null, types: val.messages?.map((msg) => msg["@type"]) ?? [], isExpedited: val.expedited, + failedReason: "", + createdHeight: null, + createdTimestamp: null, + createdTxHash: null, + description: val.summary, + proposalDeposits: [], + resolvedTimestamp: null, + version: "v1", + votingTime: null, })); -export type ProposalsResponseItemLcd = z.infer< - typeof zProposalsResponseItemLcd ->; +export type ProposalDataResponseLcd = z.infer; export const zProposalsResponseLcd = z.object({ - proposals: z.array(zProposalsResponseItemLcd), + proposals: z.array(zProposalDataResponseLcd), pagination: zPagination, }); export type ProposalsResponseLcd = z.infer; diff --git a/src/lib/services/wasm/code/index.ts b/src/lib/services/wasm/code/index.ts index 53b32bbee..748105be3 100644 --- a/src/lib/services/wasm/code/index.ts +++ b/src/lib/services/wasm/code/index.ts @@ -13,10 +13,10 @@ import type { BechAddr, BechAddr20, Option } from "lib/types"; import { getCodeData, getCodes, getCodesByAddress } from "./api"; import { getCodeLcd, getCodesLcd, getUploadAccessParamsLcd } from "./lcd"; -export const useUploadAccessParams = () => { +export const useUploadAccessParamsLcd = () => { const endpoint = useLcdEndpoint(); return useQuery( - [CELATONE_QUERY_KEYS.UPLOAD_ACCESS_PARAMS, endpoint], + [CELATONE_QUERY_KEYS.UPLOAD_ACCESS_PARAMS_LCD, endpoint], () => getUploadAccessParamsLcd(endpoint), { keepPreviousData: true, refetchOnWindowFocus: false } ); diff --git a/src/lib/types/proposal.ts b/src/lib/types/proposal.ts index 3f415801d..ba253977a 100644 --- a/src/lib/types/proposal.ts +++ b/src/lib/types/proposal.ts @@ -126,7 +126,7 @@ export interface ProposalData resolvedTimestamp: Nullable; submitTime: Date; totalDeposit: Nullable; - version: string; + version: string; // TODO: remove votingTime: Nullable; } diff --git a/src/lib/utils/proposal.ts b/src/lib/utils/proposal.ts index d2601a8eb..67d978b2f 100644 --- a/src/lib/utils/proposal.ts +++ b/src/lib/utils/proposal.ts @@ -1,6 +1,6 @@ import type { Coin } from "@cosmjs/stargate"; -import type { MinDeposit } from "lib/services/types"; +import type { GovParams } from "lib/model/proposal"; import { big } from "lib/types"; import type { Option } from "lib/types"; @@ -8,7 +8,7 @@ import { d2Formatter } from "./formatter"; export const getAmountToVote = ( initialDeposit: Coin, - minDeposit: Option + minDeposit: Option ) => { const minDepositAmount = big(minDeposit?.formattedAmount || 0); diff --git a/src/lib/utils/zod.ts b/src/lib/utils/zod.ts index 368f2867e..488281ea9 100644 --- a/src/lib/utils/zod.ts +++ b/src/lib/utils/zod.ts @@ -1,4 +1,4 @@ -import type { ZodType, ZodTypeDef } from "zod"; +import type { ZodError, ZodType, ZodTypeDef } from "zod"; export const parseWithError = ( zod: ZodType, @@ -8,7 +8,7 @@ export const parseWithError = ( return zod.parse(data); } catch (e) { // eslint-disable-next-line no-console - console.error("Zod parsing error: ", e); + console.error("Zod parsing error: ", (e as ZodError).errors); throw e; } };