diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1361881b..48ccfa59e 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
+- [#757](https://github.com/alleslabs/celatone-frontend/pull/757) api v1 - proposal data
- [#731](https://github.com/alleslabs/celatone-frontend/pull/731) Add proposal detail page structure
- [#749](https://github.com/alleslabs/celatone-frontend/pull/749) Add multi-type proposals
- [#753](https://github.com/alleslabs/celatone-frontend/pull/753) api v1 - proposal type filter
diff --git a/src/lib/app-provider/env.ts b/src/lib/app-provider/env.ts
index faf280141..d74ca2b0c 100644
--- a/src/lib/app-provider/env.ts
+++ b/src/lib/app-provider/env.ts
@@ -51,6 +51,7 @@ export enum CELATONE_QUERY_KEYS {
// FAUCET
FAUCET_INFO = "CELATONE_QUERY_FAUCET_INFO",
// X/GOV
+ PROPOSAL_DATA = "CELATONE_QUERY_PROPOSAL_DATA",
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",
diff --git a/src/lib/components/table/proposals/ProposalsTable.tsx b/src/lib/components/table/proposals/ProposalsTable.tsx
index 65ad75314..1a9531c3c 100644
--- a/src/lib/components/table/proposals/ProposalsTable.tsx
+++ b/src/lib/components/table/proposals/ProposalsTable.tsx
@@ -30,10 +30,7 @@ export const ProposalsTable = ({
return isMobile ? (
{proposals.map((proposal) => (
-
+
))}
) : (
@@ -44,7 +41,7 @@ export const ProposalsTable = ({
/>
{proposals.map((proposal) => (
@@ -101,7 +101,7 @@ export const ProposalsTableMobileCard = ({
getNavigationUrl({
type: "proposal_id",
explorerConfig: explorerLink,
- value: proposal.proposalId.toString(),
+ value: proposal.id.toString(),
lcdEndpoint,
})
);
diff --git a/src/lib/components/table/proposals/ProposalsTableRow.tsx b/src/lib/components/table/proposals/ProposalsTableRow.tsx
index 925ac4811..a0f30c1d1 100644
--- a/src/lib/components/table/proposals/ProposalsTableRow.tsx
+++ b/src/lib/components/table/proposals/ProposalsTableRow.tsx
@@ -62,7 +62,7 @@ export const ProposalsTableRow = ({
getNavigationUrl({
type: "proposal_id",
explorerConfig: explorerLink,
- value: proposal.proposalId.toString(),
+ value: proposal.id.toString(),
lcdEndpoint,
})
);
@@ -74,7 +74,7 @@ export const ProposalsTableRow = ({
diff --git a/src/lib/services/proposal.ts b/src/lib/services/proposal.ts
index 382bcee8e..74f65099a 100644
--- a/src/lib/services/proposal.ts
+++ b/src/lib/services/proposal.ts
@@ -2,7 +2,13 @@ import type { Coin } from "@cosmjs/stargate";
import axios from "axios";
import { z } from "zod";
-import { zUtcDate, zProposalType, zBechAddr, zProposalStatus } from "lib/types";
+import {
+ zBechAddr,
+ zCoin,
+ zProposalStatus,
+ zProposalType,
+ zUtcDate,
+} from "lib/types";
import type {
AccessConfigPermission,
BechAddr,
@@ -11,6 +17,7 @@ import type {
Proposal,
Option,
BechAddr20,
+ ProposalData,
ProposalStatus,
ProposalType,
} from "lib/types";
@@ -72,29 +79,18 @@ export const getProposalTypes = async (endpoint: string) =>
.get(`${endpoint}/types`)
.then(({ data }) => zProposalType.array().parse(data));
-const zProposalsResponseItem = z
- .object({
- deposit_end_time: zUtcDate,
- id: z.number().nonnegative(),
- is_expedited: z.boolean(),
- proposer: zBechAddr,
- resolved_height: z.number().nullable(),
- status: zProposalStatus,
- title: z.string(),
- types: zProposalType.array(),
- voting_end_time: zUtcDate.nullable(),
- })
- .transform((val) => ({
- depositEndTime: val.deposit_end_time,
- proposalId: val.id,
- isExpedited: val.is_expedited,
- proposer: val.proposer,
- resolvedHeight: val.resolved_height,
- status: val.status,
- title: val.title,
- types: val.types,
- votingEndTime: val.voting_end_time,
- }));
+const zProposal = z.object({
+ deposit_end_time: zUtcDate,
+ id: z.number().nonnegative(),
+ is_expedited: z.boolean(),
+ proposer: zBechAddr,
+ resolved_height: z.number().nullable(),
+ status: zProposalStatus,
+ title: z.string(),
+ types: zProposalType.array(),
+ voting_end_time: zUtcDate.nullable(),
+});
+const zProposalsResponseItem = zProposal.transform(snakeToCamel);
const zProposalsResponse = z.object({
items: z.array(zProposalsResponseItem),
@@ -164,3 +160,42 @@ export const getRelatedProposalsByContractAddress = async (
}
)
.then(({ data }) => zRelatedProposalsResponse.parse(data));
+
+const zProposalDataResponse = z.object({
+ info: zProposal
+ .extend({
+ created_height: z.number().nullable(),
+ created_timestamp: zUtcDate.nullable(),
+ created_tx_hash: z.string().nullable(),
+ description: z.string(),
+ messages: z.unknown().array().nullable(),
+ metadata: z.string(),
+ proposal_deposits: z
+ .object({
+ amount: zCoin.array(),
+ depositor: zBechAddr,
+ timestamp: zUtcDate,
+ tx_hash: z.string(),
+ })
+ .array(),
+ resolved_timestamp: zUtcDate.nullable(),
+ submit_time: zUtcDate,
+ total_deposit: zCoin.array(),
+ version: z.string(),
+ voting_time: zUtcDate.nullable(),
+ })
+ .transform(({ messages, ...val }) => ({
+ ...snakeToCamel(val),
+ messages,
+ }))
+ .nullable(),
+});
+export type ProposalDataResponse = z.infer;
+
+export const getProposalData = async (
+ endpoint: string,
+ id: number
+): Promise =>
+ axios
+ .get(`${endpoint}/${encodeURIComponent(id)}/info`)
+ .then(({ data }) => zProposalDataResponse.parse(data));
diff --git a/src/lib/services/proposalService.ts b/src/lib/services/proposalService.ts
index 0ce729a43..db01582ea 100644
--- a/src/lib/services/proposalService.ts
+++ b/src/lib/services/proposalService.ts
@@ -38,6 +38,7 @@ import { useAssetInfos } from "./assetService";
import { useMovePoolInfos } from "./move";
import type {
DepositParamsInternal,
+ ProposalDataResponse,
ProposalsResponse,
RelatedProposalsResponse,
UploadAccess,
@@ -51,6 +52,7 @@ import {
getRelatedProposalsByContractAddress,
getProposals,
getProposalTypes,
+ getProposalData,
} from "./proposal";
export const useProposals = (
@@ -163,7 +165,7 @@ export const useRelatedProposalsByModuleIdPagination = (
})
.then(({ module_proposals }) =>
module_proposals.map((proposal) => ({
- proposalId: proposal.proposal_id,
+ id: proposal.proposal_id,
title: proposal.proposal.title,
status: proposal.proposal.status as ProposalStatus,
votingEndTime: parseDate(proposal.proposal.voting_end_time),
@@ -222,6 +224,16 @@ export const useRelatedProposalsCountByModuleId = (
);
};
+export const useProposalData = (id: number) => {
+ const endpoint = useBaseApiRoute("proposals");
+
+ return useQuery(
+ [CELATONE_QUERY_KEYS.PROPOSAL_DATA, endpoint, id],
+ async () => getProposalData(endpoint, id),
+ { retry: 1, keepPreviousData: true }
+ );
+};
+
export interface MinDeposit {
amount: U>;
denom: string;
diff --git a/src/lib/types/asset.ts b/src/lib/types/asset.ts
index 73a920854..4fafdb768 100644
--- a/src/lib/types/asset.ts
+++ b/src/lib/types/asset.ts
@@ -3,6 +3,11 @@ import { z } from "zod";
import type { PoolInfo, Option, Token, U, USD } from "lib/types";
+export const zCoin = z.object({
+ denom: z.string(),
+ amount: z.string(),
+});
+
export const zAssetInfo = z.object({
coingecko: z.string(),
description: z.string(),
diff --git a/src/lib/types/proposal.ts b/src/lib/types/proposal.ts
index bdf7f66de..8efbb6b56 100644
--- a/src/lib/types/proposal.ts
+++ b/src/lib/types/proposal.ts
@@ -1,3 +1,4 @@
+import type { Coin } from "@cosmjs/amino";
import { z } from "zod";
import type { BechAddr, Nullable, Option } from "lib/types";
@@ -57,7 +58,7 @@ export const zProposalType = z.union([
export type ProposalType = z.infer;
export interface Proposal {
- proposalId: number;
+ id: number;
title: string;
status: ProposalStatus;
votingEndTime: Nullable;
@@ -67,3 +68,25 @@ export interface Proposal {
proposer: Option;
isExpedited: boolean;
}
+
+export interface ProposalDeposit {
+ amount: Coin[];
+ depositor: BechAddr;
+ timestamp: Date;
+ txHash: string;
+}
+
+export interface ProposalData extends Proposal {
+ createdHeight: Nullable;
+ createdTimestamp: Nullable;
+ createdTxHash: Nullable;
+ description: string;
+ messages: Nullable;
+ metadata: string;
+ proposalDeposits: ProposalDeposit[];
+ resolvedTimestamp: Nullable;
+ submitTime: Date;
+ totalDeposit: Coin[];
+ version: string;
+ votingTime: Nullable;
+}