Skip to content

Commit

Permalink
Merge pull request #10 from axone-protocol/feat/global-staked-overview
Browse files Browse the repository at this point in the history
feat: global staking overview
  • Loading branch information
dbatarin authored May 10, 2024
2 parents f18574a + 6394f4c commit f219c0b
Show file tree
Hide file tree
Showing 15 changed files with 125 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ REDIS_HOST=localhost
REDIS_PORT=6379

# cache
MY_STAKING_OVERVIEW=120000 #2 mins
MY_STAKING_OVERVIEW=120000 #2 mins
GLOBAL_STAKING_OVERVIEW=120000 #2 mins
1 change: 1 addition & 0 deletions src/core/config/config.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ export interface RedisConfig {

export interface CacheConfig {
myStakingOverview: number;
globalStakingOverview: number;
}
1 change: 1 addition & 0 deletions src/core/config/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export const ConfigSchema = Joi.object({
REDIS_HOST: Joi.string().required(),
REDIS_PORT: Joi.string().required(),
MY_STAKING_OVERVIEW: Joi.number().required(),
GLOBAL_STAKING_OVERVIEW: Joi.number().required(),
}).required();
1 change: 1 addition & 0 deletions src/core/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export const config: ConfigDto = {
},
cache: {
myStakingOverview: +process.env.MY_STAKING_OVERVIEW!,
globalStakingOverview: +process.env.GLOBAL_STAKING_OVERVIEW!,
}
};
12 changes: 7 additions & 5 deletions src/core/lib/okp4/enums/endpoints.enum.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export enum Endpoints {
SUPPLY_BY_DENOM = '/cosmos/bank/v1beta1/supply/by_denom',
STAKING_DELEGATIONS = '/cosmos/staking/v1beta1/delegations',
DELEGATORS_VALIDATORS = '/cosmos/staking/v1beta1/delegators/:delegator_addr/validators',
DELEGATORS_REWARDS = '/cosmos/distribution/v1beta1/delegators/:delegator_addr/rewards',
SPENDABLE_BALANCE = '/cosmos/bank/v1beta1/spendable_balances',
SUPPLY_BY_DENOM = 'cosmos/bank/v1beta1/supply/by_denom',
STAKING_DELEGATIONS = 'cosmos/staking/v1beta1/delegations',
DELEGATORS_VALIDATORS = 'cosmos/staking/v1beta1/delegators/:delegator_addr/validators',
DELEGATORS_REWARDS = 'cosmos/distribution/v1beta1/delegators/:delegator_addr/rewards',
SPENDABLE_BALANCE = 'cosmos/bank/v1beta1/spendable_balances',
VALIDATORS = 'cosmos/staking/v1beta1/validators',
TOTAL_SUPPLY = 'cosmos/bank/v1beta1/supply',
}
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 @@ -11,10 +11,12 @@ import { RouteParam } from "./enums/route-param.enum";
import { DelegatorValidatorsResponse } from "./responses/delegators-validators.response";
import { DelegatorsRewardsResponse } from "./responses/delegators-rewards.response";
import { SpendableBalancesResponse } from "./responses/spendable-balances.response";
import { SupplyResponse } from "./responses/supply.response";

@Injectable()
export class Okp4Service {
private BASE_URL = config.okp4.url;
private VALIDATORS_STATUS = 'BOND_STATUS_BONDED';

constructor(private readonly httpService: HttpService) {}

Expand Down Expand Up @@ -70,6 +72,21 @@ export class Okp4Service {
return this.getWithErrorHandling(this.constructUrl(`${Endpoints.SPENDABLE_BALANCE}/${addr}`));
}

async getValidators(): Promise<DelegatorValidatorsResponse> {
const url = this.constructUrl(
Endpoints.VALIDATORS,
createUrlParams({ status: this.VALIDATORS_STATUS }),
);
return this.getWithErrorHandling(url);
}

async getTotalSupply(): Promise<SupplyResponse> {
const url = this.constructUrl(
Endpoints.TOTAL_SUPPLY,
);
return this.getWithErrorHandling(url);
}

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

export type SupplyResponse = WithPaginationResponse<{ supply: Supply[] }>;

export interface Supply {
denom: string;
amount: string;
}
1 change: 1 addition & 0 deletions src/core/lib/osmosis/enums/endpoints.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum Endpoints {
HISTORICAL_PRICE = 'tokens/v2/historical/:symbol/chart',
TOKEN_BY_SYMBOL = 'tokens/v2/:symbol',
MARKET_CAP = 'tokens/v2/mcap',
STAKING_APR = 'apr/v2/staking',
}
6 changes: 6 additions & 0 deletions src/core/lib/osmosis/osmosis.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export class OsmosisService {
return this.getWithErrorHandling(this.constructUrl(Endpoints.MARKET_CAP));
}

async getStakingApr(): Promise<number> {
return this.getWithErrorHandling(
this.constructUrl(Endpoints.STAKING_APR),
)
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async errorHandleWrapper<T>(fn: any): Promise<T> {
try {
Expand Down
6 changes: 6 additions & 0 deletions src/modules/stacking/dtos/global-staked-overview.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface GlobalStakedOverviewDto {
totalValidators: string;
apr: string;
totalStaked: string;
bondedTokens: string;
}
1 change: 1 addition & 0 deletions src/modules/stacking/enums/stacking-endpoints.enum.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export enum StackingEndpoints {
MY_OVERVIEW = '/my/overview',
OVERVIEW = '/overview',
}
22 changes: 19 additions & 3 deletions src/modules/stacking/services/stacking.cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Cache } from 'cache-manager';
@Injectable()
export class StackingCache {
private redisStackingPrefix = 'stacking';
private globalOverviewPrefix = 'global_overview';

constructor(
@Inject(CACHE_MANAGER) private cacheService: Cache,
Expand All @@ -20,13 +21,28 @@ export class StackingCache {
const serialized = await this.cacheService.get(this.createRedisKey(address));

if (!serialized) {
return {};
return null;
}

return JSON.parse(serialized as string);
}

private createRedisKey(address: string) {
return `${this.redisStackingPrefix}_${address}`;
async setGlobalStakedOverview(data: unknown) {
const serialized = JSON.stringify(data);
await this.cacheService.set(this.createRedisKey(this.globalOverviewPrefix), serialized, config.cache.globalStakingOverview);
}

async getGlobalStakedOverview() {
const serialized = await this.cacheService.get(this.createRedisKey(this.globalOverviewPrefix));

if (!serialized) {
return null;
}

return JSON.parse(serialized as string);
}

private createRedisKey(id: string) {
return `${this.redisStackingPrefix}_${id}`;
}
}
49 changes: 48 additions & 1 deletion src/modules/stacking/services/stacking.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import { Injectable } from "@nestjs/common";
import { StackingCache } from "./stacking.cache";
import { config } from "@core/config/config";
import { MyStakedOverviewDto } from "../dtos/my-staked-overview.dto";
import { OsmosisService } from "@core/lib/osmosis/osmosis.service";
import { Validator } from "@core/lib/okp4/responses/delegators-validators.response";
import Big from "big.js";
import { GlobalStakedOverviewDto } from "../dtos/global-staked-overview.dto";

@Injectable()
export class StackingService {
constructor(
private readonly okp4Service: Okp4Service,
private readonly cache: StackingCache,
private readonly osmosisService: OsmosisService,
) { }

async getMyStakedOverview(address: string) {
Expand Down Expand Up @@ -47,7 +52,7 @@ export class StackingService {

private async fetchDelegatorsValidatorsAmount(address: string) {
const res = await this.okp4Service.getDelegatorsValidators(address);
return res.validators.length.toString();
return res.pagination.total;
}

private async fetchDelegatorsRewards(address: string) {
Expand All @@ -59,4 +64,46 @@ export class StackingService {
const res = await this.okp4Service.getSpendableBalances(address);
return res.balances.find(({ denom }) => config.app.tokenDenom === denom)?.amount || '0';
}

async getGlobalOverview() {
const cache = await this.cache.getGlobalStakedOverview();

if (cache === null) {
return this.fetchAndCacheGlobalStakedOverview();
}

return cache;
}


private async fetchAndCacheGlobalStakedOverview(): Promise<GlobalStakedOverviewDto> {
const rez = await Promise.all([
this.okp4Service.getValidators(),
this.osmosisService.getStakingApr(),
this.fetchTotalSupply(),
]);

const totalStaked = this.calculateTotalStaked(rez[0].validators);

const dto: GlobalStakedOverviewDto = {
totalValidators: rez[0].pagination.total,
apr: rez[1].toString(),
totalStaked,
bondedTokens: Big(totalStaked).div(rez[2]!.amount).toString(),
}

await this.cache.setGlobalStakedOverview(dto);

return dto;
}

private calculateTotalStaked(validators: Validator[]): string {
const totalStaked = validators.reduce((acc, val) => acc.add(val.delegator_shares), Big(0));
return totalStaked.toString();
}

private async fetchTotalSupply() {
const res = await this.okp4Service.getTotalSupply();
return res.supply.find(({ denom }) => denom === config.app.tokenDenom);
}
}
5 changes: 5 additions & 0 deletions src/modules/stacking/stacking.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ export class StackingController {
) {
return await this.service.getMyStakedOverview(address);
}

@Get(StackingEndpoints.OVERVIEW)
async getGlobalOverview() {
return this.service.getGlobalOverview();
}
}
2 changes: 2 additions & 0 deletions src/modules/stacking/stacking.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { StackingService } from "./services/stacking.service";
import { StackingController } from "./stacking.controller";
import { HttpService } from "@core/lib/http.service";
import { StackingCache } from "./services/stacking.cache";
import { OsmosisService } from "@core/lib/osmosis/osmosis.service";

@Module({
imports: [],
providers: [
Okp4Service,
OsmosisService,
StackingService,
StackingCache,
HttpService,
Expand Down

0 comments on commit f219c0b

Please sign in to comment.