Skip to content

Commit

Permalink
Merge pull request #49 from axone-protocol/refactor/staking
Browse files Browse the repository at this point in the history
refactor: staking module
  • Loading branch information
yevhen-burkovskyi authored Jul 2, 2024
2 parents a1ab214 + 4c9b74d commit eadad95
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 52 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ PROPOSALS_CACHE_TTL=120 #2 mins
PROPOSAL_CACHE_TTL=120 #2 mins
SUPPLY_CHANGE_CACHE_TTL=120 #2 mins
PROPOSAL_VOTERS_TTL=120 #2 mins
VALIDATOR_RECENTLY_PROPOSED_BLOCK_TTL=600 #10 min

# keybase
KEYBASE_URL=https://keybase.io/_/api/1.0
Expand Down
1 change: 1 addition & 0 deletions src/core/config/config.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface CacheConfig {
proposal: number;
supplyChange: number;
proposalVoters: number;
validatorRecentlyProposedBlock: number;
}

export interface KeybaseConfig {
Expand Down
1 change: 1 addition & 0 deletions src/core/config/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export const ConfigSchema = Joi.object({
EXCHANGE_RATE_BASE_URL: Joi.string().required(),
EXCHANGE_RATE_API_KEY: Joi.string().required(),
PROPOSAL_VOTERS_TTL: Joi.string().required(),
VALIDATOR_RECENTLY_PROPOSED_BLOCK_TTL: Joi.string().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 @@ -29,6 +29,7 @@ export const config: ConfigDto = {
proposal: +process.env.PROPOSAL_CACHE_TTL!,
supplyChange: +process.env.SUPPLY_CHANGE_CACHE_TTL!,
proposalVoters: +process.env.PROPOSAL_VOTERS_TTL!,
validatorRecentlyProposedBlock: +process.env.VALIDATOR_RECENTLY_PROPOSED_BLOCK_TTL!,
},
keybase: {
url: process.env.KEYBASE_URL!,
Expand Down
36 changes: 13 additions & 23 deletions src/modules/staking/services/staking.cache.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { config } from "@core/config/config";
import { Injectable } from "@nestjs/common";
import { createHash } from 'crypto';
import { StakingCachePrefix } from "../enums/staking-cache-prefix.enum";
import { RedisService } from "@core/lib/redis.service";
import { v4 } from 'uuid';
Expand All @@ -13,7 +12,7 @@ export class StakingCache {
private readonly redisService: RedisService,
) { }

private async getObjByRedisKey(key: string) {
private async getObjectFromRedis<T>(key: string): Promise<T | null> {
const serialized = await this.redisService.get(key);

if (!serialized) {
Expand All @@ -29,7 +28,7 @@ export class StakingCache {
}

async getMyStakedOverview(address: string) {
return this.getObjByRedisKey(this.createRedisKey(address));
return this.getObjectFromRedis(this.createRedisKey(address));
}

async setGlobalStakedOverview(data: unknown) {
Expand All @@ -38,7 +37,7 @@ export class StakingCache {
}

async getGlobalStakedOverview() {
return this.getObjByRedisKey(this.createRedisKey(StakingCachePrefix.GLOBAL_OVERVIEW));
return this.getObjectFromRedis(this.createRedisKey(StakingCachePrefix.GLOBAL_OVERVIEW));
}

async setValidators(validators: unknown[]) {
Expand All @@ -47,28 +46,19 @@ export class StakingCache {
}

async getValidators() {
return this.getObjByRedisKey(this.createRedisKey(StakingCachePrefix.VALIDATORS));
return this.getObjectFromRedis(this.createRedisKey(StakingCachePrefix.VALIDATORS));
}

async setValidatorDelegation(address: string, validatorAddress: string, data: unknown) {
const serialized = JSON.stringify(data);
const hash = this.createValidatorDelegationHash(address, validatorAddress);
async setValidatorDelegation(hash: string, data: unknown) {
await this.redisService.setWithTTL(
this.createRedisKey(hash),
serialized,
JSON.stringify(data),
config.cache.validators
);
}

async getValidatorDelegation(address: string, validatorAddress: string) {
const hash = this.createValidatorDelegationHash(address, validatorAddress);
return this.getObjByRedisKey(this.createRedisKey(hash));
}

private createValidatorDelegationHash(address: string, validatorAddress: string) {
const hash = createHash('sha256');
hash.update(`${address}_${validatorAddress}`);
return hash.digest('hex');
async getValidatorDelegation(hash: string) {
return this.getObjectFromRedis(this.createRedisKey(hash));
}

async setValidatorImg(id: string, imgUrl: string) {
Expand All @@ -94,7 +84,7 @@ export class StakingCache {

async setRecentlyProposedBlock(address: string, block: unknown) {
const key = this.createRedisKey(this.createRedisKey(StakingCachePrefix.VALIDATOR_RECENTLY_PROPOSED_BLOCKS, address), v4());
this.redisService.setWithTTL(key, JSON.stringify(block), config.cache.validatorSignature);
this.redisService.setWithTTL(key, JSON.stringify(block), config.cache.validatorRecentlyProposedBlock);
}

async getRecentlyProposedBlock(address: string) {
Expand Down Expand Up @@ -128,17 +118,17 @@ export class StakingCache {
await this.redisService.setWithTTL(this.createRedisKey(StakingCachePrefix.PROPOSALS), serialized, config.cache.proposals);
}

async getProposals(): Promise<GetProposalsResponse> {
return this.getObjByRedisKey(this.createRedisKey(StakingCachePrefix.PROPOSALS));
async getProposals() {
return this.getObjectFromRedis(this.createRedisKey(StakingCachePrefix.PROPOSALS));
}

async setProposal(proposalId: string | number, proposal: GetProposalResponse) {
const serialized = JSON.stringify(proposal);
await this.redisService.setWithTTL(this.createRedisKey(StakingCachePrefix.PROPOSAL, String(proposalId)), serialized, config.cache.proposal);
}

async getProposal(proposalId: string | number): Promise<GetProposalResponse> {
return this.getObjByRedisKey(this.createRedisKey(StakingCachePrefix.PROPOSAL, String(proposalId)));
async getProposal(proposalId: string | number) {
return this.getObjectFromRedis(this.createRedisKey(StakingCachePrefix.PROPOSAL, String(proposalId)));
}

async setProposalVotes(hash: string, voters: unknown[]) {
Expand Down
55 changes: 26 additions & 29 deletions src/modules/staking/services/staking.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,7 @@ export class StakingService implements OnModuleInit {
)
.reduce(
(acc, val) =>
acc +
+(
val.reward.find(({ denom }) => config.app.tokenDenom === denom)
?.amount || 0
),
acc + Number.parseFloat(val.reward.find(({ denom }) => config.app.tokenDenom === denom)?.amount || "0"),
0
);
}
Expand All @@ -144,7 +140,7 @@ export class StakingService implements OnModuleInit {
}

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

if (cache === null) {
return this.fetchAndCacheGlobalStakedOverview();
Expand All @@ -167,9 +163,7 @@ export class StakingService implements OnModuleInit {
totalValidators: rez[0].pagination.total,
apr: rez[1],
totalStaked,
bondedTokens: toPercents(
Big(rez[3].pool.bonded_tokens).div(rez[2]!.amount)
),
bondedTokens: toPercents(Big(rez[3].pool.bonded_tokens).div(rez[2]!.amount)),
};

await this.cache.setGlobalStakedOverview(dto);
Expand All @@ -191,7 +185,7 @@ export class StakingService implements OnModuleInit {
}

async getValidators() {
const cache = await this.cache.getValidators();
const cache = await this.cache.getValidators() as Validator[];

if (cache === null) {
return this.fetchAndCacheValidators();
Expand All @@ -211,19 +205,13 @@ export class StakingService implements OnModuleInit {
toView: Validator[]
): Promise<ValidatorsViewDto[]> {
const view = [];
const globalOverview: GlobalStakedOverviewDto =
await this.getGlobalOverview();
const globalOverview: GlobalStakedOverviewDto = await this.getGlobalOverview();

for (const validator of toView) {
const uptime = await this.calculateValidatorUptime(
validator.operator_address
);
const votingPower = Big(validator.delegator_shares)
.div(globalOverview.totalStaked)
.toNumber();
const logo = (await this.cache.getValidatorImg(
validator.description.identity
)) as string;
const uptime = await this.calculateValidatorUptime(validator.operator_address);
const votingPower = Big(validator.delegator_shares).div(globalOverview.totalStaked).toNumber();
const logo = (await this.cache.getValidatorImg(validator.description.identity)) as string;

view.push({
logo,
description: {
Expand All @@ -245,18 +233,18 @@ export class StakingService implements OnModuleInit {
commission: {
updateTime: validator.commission.update_time,
rate: toPercents(validator.commission.commission_rates.rate),
maxChangeRate: validator.commission.commission_rates.max_change_rate,
maxRate: validator.commission.commission_rates.max_rate,
maxChangeRate: toPercents(validator.commission.commission_rates.max_change_rate),
maxRate: toPercents(validator.commission.commission_rates.max_rate),
},
});
}

return view;
}

async getMyValidatorDelegation(payload: MyValidatorDelegationDto) {
const cache = await this.cache.getValidatorDelegation(
payload.address,
payload.validatorAddress
this.createValidatorDelegationHash(payload.address, payload.validatorAddress)
);

if (cache === null) {
Expand All @@ -280,14 +268,19 @@ export class StakingService implements OnModuleInit {
};

await this.cache.setValidatorDelegation(
payload.address,
payload.validatorAddress,
this.createValidatorDelegationHash(payload.address, payload.validatorAddress),
dto
);

return dto;
}

private createValidatorDelegationHash(address: string, validatorAddress: string) {
const hash = createHash('sha256');
hash.update(`${address}_${validatorAddress}`);
return hash.digest('hex');
}

async getValidatorDelegations(payload: ValidatorDelegationsDto) {
const res = await this.okp4Service.getValidatorDelegations(
payload.address,
Expand Down Expand Up @@ -433,7 +426,7 @@ export class StakingService implements OnModuleInit {
return {
height: res.block.last_commit.height,
blockHash: res.block.last_commit.block_id.hash,
txs: res.block.data.txs.length,
txs: String(res.block.data.txs.length),
time: new Date(),
};
}
Expand All @@ -459,7 +452,11 @@ export class StakingService implements OnModuleInit {
private async createValidatorAddrToPubkeyMap() {
const map = new Map();
await this.fetchAndCacheValidators();
const validators: Validator[] = await this.cache.getValidators();
const validators = await this.cache.getValidators() as Validator[];

if(!validators) {
Log.warn('Validators list empty');
}

for (const validator of validators) {
const pubkey = this.okp4Service
Expand Down

0 comments on commit eadad95

Please sign in to comment.