Skip to content

Commit

Permalink
bug: governance and unbonding fixes (#1044)
Browse files Browse the repository at this point in the history
* refactor: use new api client

* feat: use maxBlockTime to calculate unbonding period

* feat: properly disable vote button and poll proposal changes

* chore: fix PR comments
  • Loading branch information
mateuszjasiuk authored Aug 29, 2024
1 parent 96860cd commit 976fe32
Show file tree
Hide file tree
Showing 14 changed files with 108 additions and 46 deletions.
2 changes: 1 addition & 1 deletion apps/namadillo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license": "MIT",
"private": true,
"dependencies": {
"@anomaorg/namada-indexer-client": "0.0.21",
"@anomaorg/namada-indexer-client": "0.0.23",
"@cosmjs/encoding": "^0.32.3",
"@tailwindcss/container-queries": "^0.1.1",
"@tanstack/react-query": "^5.40.0",
Expand Down
4 changes: 3 additions & 1 deletion apps/namadillo/src/App/Governance/ProposalHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,9 @@ const VoteButton: React.FC<{
}> = ({ proposal, voted, proposalId }) => {
const navigate = useNavigate();
const isExtensionConnected = useAtomValue(namadaExtensionConnectedAtom);
const canVote = useAtomValue(canVoteAtom);
const canVote = useAtomValue(
canVoteAtom(proposal.data?.startEpoch || BigInt(-1))
);

if (!isExtensionConnected) {
return null;
Expand Down
7 changes: 6 additions & 1 deletion apps/namadillo/src/App/Governance/ProposalStatusSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import BigNumber from "bignumber.js";
import clsx from "clsx";
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";
import { useEffect, useState } from "react";

import { PieChart, PieChartData, Stack } from "@namada/components";
import { formatPercentage } from "@namada/utils";
Expand Down Expand Up @@ -165,6 +165,11 @@ const Loaded: React.FC<{
highestVoteType
);

useEffect(() => {
// Reset the hovered vote type when the highest vote type changes(on data poll)
setHoveredVoteType(highestVoteType);
}, [highestVoteType]);

const votedProportion =
totalVotingPower.isEqualTo(0) ?
BigNumber(0)
Expand Down
3 changes: 2 additions & 1 deletion apps/namadillo/src/App/Governance/SubmitVote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ export const WithProposalId: React.FC<{ proposalId: bigint }> = ({
const [selectedVoteType, setSelectedVoteType] = useState<VoteType>();

const proposalQueryResult = useAtomValue(proposalFamily(proposalId));
const canVote = useAtomValue(canVoteAtom);

const proposal =
proposalQueryResult.isSuccess ? proposalQueryResult.data : null;

const canVote = useAtomValue(canVoteAtom(proposal?.startEpoch || BigInt(-1)));

const onCloseModal = (): void => navigate(-1);

const dispatchPendingNotification = (txs: TxProps[]): void => {
Expand Down
6 changes: 4 additions & 2 deletions apps/namadillo/src/atoms/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DefaultApi } from "@anomaorg/namada-indexer-client";
import { Configuration, DefaultApi } from "@anomaorg/namada-indexer-client";
import { Atom, atom, getDefaultStore } from "jotai";
import { indexerUrlAtom } from "./settings";

Expand All @@ -14,5 +14,7 @@ export const getIndexerApi = (): DefaultApi => {
// Helper function to use outside of hooks
const getApi = (get: <Value>(atom: Atom<Value>) => Value): DefaultApi => {
const indexerUrl = get(indexerUrlAtom);
return new DefaultApi({ basePath: indexerUrl });
const configuration = new Configuration({ basePath: indexerUrl });

return new DefaultApi(configuration);
};
1 change: 1 addition & 0 deletions apps/namadillo/src/atoms/chain/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const fetchChainParameters = async (
1,
minEpochDuration: Number(parameters.minDuration),
minNumOfBlocks: Number(parameters.minNumOfBlocks),
maxBlockTime: Number(parameters.maxBlockTime),
epochSwitchBlocksDelay: Number(parameters.epochSwitchBlocksDelay),
},
apr: BigNumber(parameters.apr),
Expand Down
9 changes: 8 additions & 1 deletion apps/namadillo/src/atoms/etc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { atomWithStorage } from "jotai/utils";

type ControlRoutineProps = {
shouldUpdateAmount: boolean;
shouldUpdateProposal: boolean;
lastBlockHeight: BigNumber | undefined;
};

export const controlRoutineAtom = atomWithStorage<ControlRoutineProps>(
"namadillo:etc",
{
shouldUpdateAmount: false,
shouldUpdateProposal: false,
lastBlockHeight: undefined,
}
);
Expand All @@ -29,7 +31,12 @@ export const shouldUpdateBalanceAtom = atom(
changeProps<boolean>("shouldUpdateAmount")
);

export const shouldUpdateProposalAtom = atom(
(get) => get(controlRoutineAtom).shouldUpdateProposal,
changeProps<boolean>("shouldUpdateProposal")
);

export const lastBlockHeightAtom = atom(
(get) => get(controlRoutineAtom).shouldUpdateAmount,
(get) => get(controlRoutineAtom).lastBlockHeight,
changeProps<BigNumber | undefined>("lastBlockHeight")
);
51 changes: 30 additions & 21 deletions apps/namadillo/src/atoms/proposals/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ import {
fetchVotedProposalIds,
} from "./functions";

import {
Bond as NamadaIndexerBond,
BondStatusEnum as NamadaIndexerBondStatusEnum,
} from "@anomaorg/namada-indexer-client";
import { Bond as NamadaIndexerBond } from "@anomaorg/namada-indexer-client";
import { shouldUpdateProposalAtom } from "atoms/etc";
export const proposalFamily = atomFamily((id: bigint) =>
atomWithQuery((get) => {
const api = get(indexerApiAtom);
const enablePolling = get(shouldUpdateProposalAtom);

return {
// TODO: subscribe to indexer events when it's done
refetchInterval: enablePolling ? 1000 : false,
queryKey: ["proposal", id.toString()],
queryFn: () => fetchProposalById(api, id),
};
Expand Down Expand Up @@ -96,7 +97,11 @@ export const paginatedProposalsFamily = atomFamily(
export const votedProposalIdsAtom = atomWithQuery((get) => {
const account = get(defaultAccountAtom);
const api = get(indexerApiAtom);
const enablePolling = get(shouldUpdateProposalAtom);

return {
// TODO: subscribe to indexer events when it's done
refetchInterval: enablePolling ? 1000 : false,
queryKey: ["voted-proposal-ids", account.data],
...queryDependentFn(async () => {
if (typeof account.data === "undefined") {
Expand All @@ -107,25 +112,29 @@ export const votedProposalIdsAtom = atomWithQuery((get) => {
};
});

export const canVoteAtom = atomWithQuery((get) => {
const account = get(defaultAccountAtom);
const api = get(indexerApiAtom);
export const canVoteAtom = atomFamily((proposalStartEpoch: bigint) =>
atomWithQuery((get) => {
const account = get(defaultAccountAtom);
const api = get(indexerApiAtom);

return {
queryKey: ["can-vote"],
enabled: account.isSuccess,
queryFn: async () => {
const all_bonds = await api.apiV1PosBondAddressGet(account.data!.address);
return {
queryKey: ["can-vote", account.data, api],
enabled: account.isSuccess,
queryFn: async () => {
const all_bonds = await api.apiV1PosBondAddressGet(
account.data!.address
);

return all_bonds.data.results.reduce(
(acc: boolean, current: NamadaIndexerBond) => {
return acc || current.status === NamadaIndexerBondStatusEnum.Active;
},
false
);
},
};
});
return all_bonds.data.results.reduce(
(acc: boolean, current: NamadaIndexerBond) => {
return acc || Number(current.startEpoch) <= proposalStartEpoch;
},
false
);
},
};
})
);

type CreateVoteTxArgs = {
proposalId: bigint;
Expand Down
11 changes: 6 additions & 5 deletions apps/namadillo/src/atoms/proposals/functions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ApiV1GovProposalGetStatusEnum as ApiIndexerProposalStatusEnum,
DefaultApi,
Proposal as IndexerProposal,
ProposalStatusEnum as IndexerProposalStatusEnum,
Expand Down Expand Up @@ -257,16 +258,16 @@ const fromIndexerStatus = (

const toIndexerStatus = (
proposalStatus: ProposalStatus
): IndexerProposalStatusEnum => {
): ApiIndexerProposalStatusEnum => {
switch (proposalStatus) {
case "pending":
return IndexerProposalStatusEnum.Pending;
return ApiIndexerProposalStatusEnum.Pending;
case "ongoing":
return IndexerProposalStatusEnum.Voting;
return ApiIndexerProposalStatusEnum.VotingPeriod;
case "passed":
return IndexerProposalStatusEnum.Passed;
return ApiIndexerProposalStatusEnum.Passed;
case "rejected":
return IndexerProposalStatusEnum.Rejected;
return ApiIndexerProposalStatusEnum.Rejected;
default:
return assertNever(proposalStatus);
}
Expand Down
5 changes: 3 additions & 2 deletions apps/namadillo/src/atoms/settings/services.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DefaultApi } from "@anomaorg/namada-indexer-client";
import { Configuration, DefaultApi } from "@anomaorg/namada-indexer-client";
import { isUrlValid } from "@namada/utils";
import toml from "toml";
import { SettingsTomlOptions } from "types";
Expand All @@ -8,7 +8,8 @@ export const isIndexerAlive = async (url: string): Promise<boolean> => {
return false;
}
try {
const api = new DefaultApi({ basePath: url });
const configuration = new Configuration({ basePath: url });
const api = new DefaultApi(configuration);
const response = await api.healthGet();
return response.status === 200;
} catch {
Expand Down
8 changes: 4 additions & 4 deletions apps/namadillo/src/atoms/validators/functions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Bond as IndexerBond,
MergedBond as IndexerMergedBond,
Unbond as IndexerUnbond,
Validator as IndexerValidator,
VotingPower as IndexerVotingPower,
Expand All @@ -18,10 +19,9 @@ export const toValidator = (
const expectedApr = nominalApr.times(1 - commission.toNumber());

// Because epoch duration is in reality longer by epochSwitchBlocksDelay we have to account for that
const timePerBlock = epochInfo.minEpochDuration / epochInfo.minNumOfBlocks;
const realMinEpochDuration =
epochInfo.minEpochDuration +
timePerBlock * epochInfo.epochSwitchBlocksDelay;
epochInfo.maxBlockTime * epochInfo.epochSwitchBlocksDelay;

const unbondingPeriod = singleUnitDurationFromInterval(
0,
Expand Down Expand Up @@ -62,7 +62,7 @@ export const calculateUnbondingTimeLeft = (unbond: IndexerUnbond): string => {
* an array of MyValidators objects
*/
export const toMyValidators = (
indexerBonds: IndexerBond[],
indexerBonds: IndexerBond[] | IndexerMergedBond[],
indexerUnbonds: IndexerUnbond[],
totalVotingPower: IndexerVotingPower,
epochInfo: EpochInfo,
Expand All @@ -86,7 +86,7 @@ export const toMyValidators = (
const addBondToAddress = (
address: Address,
key: "bondItems" | "unbondItems",
bond: IndexerBond | IndexerUnbond
bond: IndexerBond | IndexerMergedBond | IndexerUnbond
): void => {
const { validator: _, ...bondsWithoutValidator } = bond;
myValidators[address]![key].push(bondsWithoutValidator);
Expand Down
13 changes: 12 additions & 1 deletion apps/namadillo/src/hooks/useTransactionCallbacks.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { accountBalanceAtom } from "atoms/accounts";
import { shouldUpdateBalanceAtom } from "atoms/etc";
import { shouldUpdateBalanceAtom, shouldUpdateProposalAtom } from "atoms/etc";
import { useAtomValue, useSetAtom } from "jotai";
import { useTransactionEventListener } from "utils";

Expand All @@ -19,4 +19,15 @@ export const useTransactionCallback = (): void => {
useTransactionEventListener("Bond.Success", onBalanceUpdate);
useTransactionEventListener("Unbond.Success", onBalanceUpdate);
useTransactionEventListener("Withdraw.Success", onBalanceUpdate);

const shouldUpdateProposal = useSetAtom(shouldUpdateProposalAtom);

useTransactionEventListener("VoteProposal.Success", () => {
shouldUpdateProposal(true);

// This does not guarantee that the proposal will be updated,
// but because this is temporary solution(don't quote me on this), it should be fine :)
const timePolling = 12 * 1000;
setTimeout(() => shouldUpdateProposal(false), timePolling);
});
};
1 change: 1 addition & 0 deletions apps/namadillo/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export type EpochInfo = {
unbondingPeriodInEpochs: number;
minEpochDuration: number;
minNumOfBlocks: number;
maxBlockTime: number;
epochSwitchBlocksDelay: number;
};

Expand Down
33 changes: 27 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ __metadata:
languageName: node
linkType: hard

"@anomaorg/namada-indexer-client@npm:0.0.21":
version: 0.0.21
resolution: "@anomaorg/namada-indexer-client@npm:0.0.21"
"@anomaorg/namada-indexer-client@npm:0.0.23":
version: 0.0.23
resolution: "@anomaorg/namada-indexer-client@npm:0.0.23"
dependencies:
axios: "npm:^0.21.1"
checksum: 4b9632145eee1d20672ad65eef447ed90e3abfc2fee09a5c3e40fc5fd9321ff83aff03acf4be75abe1c74c83fc635216f46f5c58155c11a291656bb822f05465
axios: "npm:^1.6.1"
checksum: 1249a851ad5ad09daa88bc621fbc33fe520a3f5912ab02d472c4d7b52f26fb83cee2f7818c634a55abb137dfdf5226d0ecd057e24895573199da069a5111e1cd
languageName: node
linkType: hard

Expand Down Expand Up @@ -8448,7 +8448,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@namada/namadillo@workspace:apps/namadillo"
dependencies:
"@anomaorg/namada-indexer-client": "npm:0.0.21"
"@anomaorg/namada-indexer-client": "npm:0.0.23"
"@cosmjs/encoding": "npm:^0.32.3"
"@playwright/test": "npm:^1.24.1"
"@release-it/keep-a-changelog": "npm:^5.0.0"
Expand Down Expand Up @@ -12954,6 +12954,17 @@ __metadata:
languageName: node
linkType: hard

"axios@npm:^1.6.1":
version: 1.7.4
resolution: "axios@npm:1.7.4"
dependencies:
follow-redirects: "npm:^1.15.6"
form-data: "npm:^4.0.0"
proxy-from-env: "npm:^1.1.0"
checksum: 5ea1a93140ca1d49db25ef8e1bd8cfc59da6f9220159a944168860ad15a2743ea21c5df2967795acb15cbe81362f5b157fdebbea39d53117ca27658bab9f7f17
languageName: node
linkType: hard

"axobject-query@npm:^2.2.0":
version: 2.2.0
resolution: "axobject-query@npm:2.2.0"
Expand Down Expand Up @@ -19364,6 +19375,16 @@ __metadata:
languageName: node
linkType: hard

"follow-redirects@npm:^1.15.6":
version: 1.15.6
resolution: "follow-redirects@npm:1.15.6"
peerDependenciesMeta:
debug:
optional: true
checksum: 9ff767f0d7be6aa6870c82ac79cf0368cd73e01bbc00e9eb1c2a16fbb198ec105e3c9b6628bb98e9f3ac66fe29a957b9645bcb9a490bb7aa0d35f908b6b85071
languageName: node
linkType: hard

"for-each@npm:^0.3.3":
version: 0.3.3
resolution: "for-each@npm:0.3.3"
Expand Down

0 comments on commit 976fe32

Please sign in to comment.