Skip to content

Commit

Permalink
Merge pull request #30 from axone-protocol/feat/gov-overview
Browse files Browse the repository at this point in the history
feat: added gov overview endpoint
  • Loading branch information
dbatarin authored Jun 3, 2024
2 parents 383ce3e + 740c488 commit 6400ec3
Show file tree
Hide file tree
Showing 16 changed files with 265 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/core/enums/routes.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum Routes {
SUPPLY = 'supply',
STAKING = 'staking',
TOKEN = 'token',
GOVERNANCE = 'governance',
}
2 changes: 2 additions & 0 deletions src/core/lib/okp4/enums/endpoints.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ export enum Endpoints {
VALIDATOR_DELEGATIONS = 'cosmos/staking/v1beta1/validators/:validator_addr/delegations',
BLOCKS_LATEST = 'cosmos/base/tendermint/v1beta1/blocks/latest',
BLOCKS_BY_HEIGHT = 'cosmos/base/tendermint/v1beta1/blocks/:height',
GOV_PARAMS = 'cosmos/gov/v1/params/:params_type',
GOV_PROPOSALS = 'cosmos/gov/v1/proposals',
}
5 changes: 5 additions & 0 deletions src/core/lib/okp4/enums/gov-type.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum GovType {
VOTING = 'voting',
TALLYING = 'tallying',
DEPOSIT = 'deposit'
}
8 changes: 8 additions & 0 deletions src/core/lib/okp4/enums/proposal-status.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum ProposalStatusEnum {
PROPOSAL_STATUS_UNSPECIFIED = 'PROPOSAL_STATUS_UNSPECIFIED',
PROPOSAL_STATUS_DEPOSIT_PERIOD = 'PROPOSAL_STATUS_DEPOSIT_PERIOD',
PROPOSAL_STATUS_VOTING_PERIOD = 'PROPOSAL_STATUS_VOTING_PERIOD',
PROPOSAL_STATUS_PASSED = 'PROPOSAL_STATUS_PASSED',
PROPOSAL_STATUS_REJECTED = 'PROPOSAL_STATUS_REJECTED',
PROPOSAL_STATUS_FAILED = 'PROPOSAL_STATUS_FAILED',
}
1 change: 1 addition & 0 deletions src/core/lib/okp4/enums/route-param.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum RouteParam {
DELEGATOR_ADDRES = ':delegator_addr',
VALIDATOR_ADDRES = ':validator_addr',
HEIGHT = ':height',
PARAMS_TYPE = ':params_type',
}
17 changes: 17 additions & 0 deletions src/core/lib/okp4/okp4.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { BlocksResponse } from "./responses/blocks.response";
import { WebSocket } from 'ws';
import { Log } from "@core/loggers/log";
import { EventEmitter2 } from "@nestjs/event-emitter";
import { GovType } from "./enums/gov-type.enum";
import { GovParamsResponse } from "./responses/gov-params.response";
import { GetProposalsResponse } from "./responses/get-proposals.response";

@Injectable()
export class Okp4Service {
Expand Down Expand Up @@ -172,6 +175,20 @@ export class Okp4Service {
});
}

async getGovParams(type = GovType.VOTING): Promise<GovParamsResponse> {
return this.getWithErrorHandling(
this.constructUrl(
Endpoints.GOV_PARAMS.replace(RouteParam.PARAMS_TYPE, type)
)
);
}

async getProposals(): Promise<GetProposalsResponse> {
return this.getWithErrorHandling(
this.constructUrl(Endpoints.GOV_PROPOSALS)
);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async errorHandleWrapper<T>(fn: any): Promise<T> {
try {
Expand Down
43 changes: 43 additions & 0 deletions src/core/lib/okp4/responses/get-proposals.response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { WithPaginationResponse } from "./with-pagination.response"

export type GetProposalsResponse = WithPaginationResponse<{ proposals: Proposal[] }>;

export interface Proposal {
id: string;
messages: [
{
"@type": string;
authority: string;
params: {
mint_denom: string;
inflation_coef: string;
blocks_per_year: string;
inflation_max: unknown;
inflation_min: unknown;
}
}
];
status: string;
final_tally_result: {
yes_count: string;
abstain_count: string;
no_count: string;
no_with_veto_count: string;
};
submit_time: string;
deposit_end_time: string;
total_deposit: [
{
denom: string;
amount: string;
}
];
voting_start_time: string;
voting_end_time: string;
metadata: string;
title: string;
summary: string;
proposer: string;
expedited: boolean;
failed_reason: string;
}
35 changes: 35 additions & 0 deletions src/core/lib/okp4/responses/gov-params.response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export interface GovParamsResponse {
voting_params: {
voting_period: string;
};
deposit_params: unknown;
tally_params: unknown;
params: {
min_deposit: [
{
denom: string;
amount: string;
}
];
max_deposit_period: string;
voting_period: string;
quorum: string;
threshold: string;
veto_threshold: string;
min_initial_deposit_ratio: string;
proposal_cancel_ratio: string;
proposal_cancel_dest: string;
expedited_voting_period: string;
expedited_threshold: string;
expedited_min_deposit: [
{
denom: string;
amount: string;
}
];
burn_vote_quorum: boolean;
burn_proposal_deposit_prevote: boolean;
burn_vote_veto: boolean;
min_deposit_ratio: string;
}
}
2 changes: 2 additions & 0 deletions src/modules/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EventEmitterModule } from '@nestjs/event-emitter';
import { SupplyModule } from './supply/supply.module';
import { StakingModule } from './staking/staking.module';
import { TokenModule } from './token/token.module';
import { GovernanceModule } from './governance/governance.module';

@Module({
imports: [
Expand All @@ -13,6 +14,7 @@ import { TokenModule } from './token/token.module';
TokenModule,
SupplyModule,
StakingModule,
GovernanceModule,
],
})
export class AppModule {}
6 changes: 6 additions & 0 deletions src/modules/governance/dto/gov-overview.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface GovOverviewDto {
totalProposals: number;
currentProposals: number;
votingPeriod: string;
depositRequired: string;
}
3 changes: 3 additions & 0 deletions src/modules/governance/enums/governance-endpoint.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum GovernanceEndpoint {
OVERVIEW = 'overview',
}
14 changes: 14 additions & 0 deletions src/modules/governance/governance.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Controller, Get } from "@nestjs/common";
import { GovernanceCache } from "./services/governance.cache";
import { GovernanceEndpoint } from "./enums/governance-endpoint.enum";
import { Routes } from "@core/enums/routes.enum";

@Controller(Routes.GOVERNANCE)
export class GovernanceController {
constructor(private readonly cache: GovernanceCache) { }

@Get(GovernanceEndpoint.OVERVIEW)
async overview() {
return this.cache.getGovOverview();
}
}
21 changes: 21 additions & 0 deletions src/modules/governance/governance.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Module } from "@nestjs/common";
import { GovernanceController } from "./governance.controller";
import { GovernanceCache } from "./services/governance.cache";
import { GovernanceService } from "./services/governance.service";
import { RedisService } from "@core/lib/redis.service";
import { Okp4Service } from "@core/lib/okp4/okp4.service";
import { HttpService } from "@core/lib/http.service";
import { GovernanceJobs } from "./services/governance.jobs";

@Module({
controllers: [GovernanceController],
providers: [
RedisService,
GovernanceCache,
GovernanceService,
Okp4Service,
HttpService,
GovernanceJobs,
]
})
export class GovernanceModule {}
30 changes: 30 additions & 0 deletions src/modules/governance/services/governance.cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { RedisService } from "@core/lib/redis.service";
import { Injectable } from "@nestjs/common";
import { GovOverviewDto } from "../dto/gov-overview.dto";

@Injectable()
export class GovernanceCache {
private governanceCachePrefix = 'governance';

constructor(
private readonly redisService: RedisService,
) { }

async setGovOverview(overview: GovOverviewDto) {
this.redisService.set(this.createRedisKey('overview'), JSON.stringify(overview));
}

async getGovOverview() {
const res = await this.redisService.get(this.createRedisKey('overview'));

if (res) {
return JSON.parse(res);
}

return null;
}

private createRedisKey(...ids: string[]) {
return `${this.governanceCachePrefix}${ids.map(id => `_${id}`).join('')}`
}
}
15 changes: 15 additions & 0 deletions src/modules/governance/services/governance.jobs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Injectable } from "@nestjs/common";
import { Cron } from "@nestjs/schedule";
import { GovernanceService } from "./governance.service";

@Injectable()
export class GovernanceJobs {
constructor(
private readonly service: GovernanceService,
) { }

@Cron('*/5 * * * *')
async updateGovOverview() {
await this.service.fetchAndCacheGovOverview();
}
}
62 changes: 62 additions & 0 deletions src/modules/governance/services/governance.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Okp4Service } from "@core/lib/okp4/okp4.service";
import { Injectable, OnModuleInit } from "@nestjs/common";
import { GovOverviewDto } from "../dto/gov-overview.dto";
import { Proposal } from "@core/lib/okp4/responses/get-proposals.response";
import { ProposalStatusEnum } from "@core/lib/okp4/enums/proposal-status.enum";
import { GovernanceCache } from "./governance.cache";
import { Log } from "@core/loggers/log";

@Injectable()
export class GovernanceService implements OnModuleInit {
constructor(
private readonly okp4Service: Okp4Service,
private readonly cache: GovernanceCache,
) { }

async onModuleInit() {
await this.fetchAndCacheGovOverview();
}

async fetchAndCacheGovOverview() {
try {
const govResponse = await this.okp4Service.getGovParams();
const govProposals = await this.okp4Service.getProposals();

const govOverview: GovOverviewDto = {
totalProposals: Number.parseInt(govProposals.pagination.total),
currentProposals: this.currentProposals(govProposals.proposals),
votingPeriod: this.votingPeriodToView(govResponse.voting_params.voting_period),
depositRequired: govResponse?.params?.min_deposit[0]?.amount || '0',
}

this.cache.setGovOverview(govOverview);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
Log.warn("Failed to fetch and cache gov overview " + e.message);
}
}

private currentProposals(proposals: Proposal[]) {
const activeStatuses: string[] = [
ProposalStatusEnum.PROPOSAL_STATUS_DEPOSIT_PERIOD,
ProposalStatusEnum.PROPOSAL_STATUS_UNSPECIFIED,
ProposalStatusEnum.PROPOSAL_STATUS_VOTING_PERIOD,
];
return proposals.reduce((acc, val) => {
if (activeStatuses.includes(val.status)) {
let count = acc;
count += 1;
return count;
}
return acc;
}, 0)
}

private votingPeriodToView(votingPeriod: string) {
const totalSeconds = Number.parseInt(votingPeriod.slice(0, -1), 10);

const days = totalSeconds / 86400;

return `${Number.isInteger(days) ? days : days.toFixed(1)} D`;
}
}

0 comments on commit 6400ec3

Please sign in to comment.