Skip to content

Commit

Permalink
feat(#495): Add monitoring for claimable rewards and vehnt
Browse files Browse the repository at this point in the history
  • Loading branch information
ChewingGlass committed Dec 11, 2023
1 parent cff8984 commit dbd2f96
Show file tree
Hide file tree
Showing 16 changed files with 587 additions and 40 deletions.
2 changes: 2 additions & 0 deletions packages/distributor-oracle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"fastify": "^4.13.0",
"fastify-cron": "^1.3.1",
"ky": "^0.31.4",
"pg": "^8.9.0",
"prom-client": "^15.0.0",
"sequelize": "^6.28.0",
"typescript-collections": "^1.3.3"
},
Expand Down
9 changes: 9 additions & 0 deletions packages/distributor-oracle/src/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import client from "prom-client";

export const register = new client.Registry();
export const totalRewardsGauge = new client.Gauge({
name: "total_rewards",
help: "Total number of rewards",
labelNames: ["dnt_mint"],
});
register.registerMetric(totalRewardsGauge);
126 changes: 91 additions & 35 deletions packages/distributor-oracle/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dotenv from "dotenv";
import bs58 from "bs58";
import fastifyCron from "fastify-cron";
dotenv.config();
// @ts-ignore
import {
Expand All @@ -9,7 +9,7 @@ import {
getProvider,
Instruction,
Program,
setProvider
setProvider,
} from "@coral-xyz/anchor";
import {
decodeEntityKey,
Expand All @@ -19,23 +19,34 @@ import {
keyToAssetForAsset,
} from "@helium/helium-entity-manager-sdk";
import { daoKey } from "@helium/helium-sub-daos-sdk";
import {
HeliumEntityManager,
} from "@helium/idls/lib/types/helium_entity_manager";
import { HeliumEntityManager } from "@helium/idls/lib/types/helium_entity_manager";
import { LazyDistributor } from "@helium/idls/lib/types/lazy_distributor";
import { init as initLazy, lazyDistributorKey, PROGRAM_ID as LD_PID } from "@helium/lazy-distributor-sdk";
import { init as initRewards, PROGRAM_ID as RO_PID} from "@helium/rewards-oracle-sdk";
import { Asset, getAsset, HNT_MINT, IOT_MINT } from "@helium/spl-utils";
import {
init as initLazy,
lazyDistributorKey,
PROGRAM_ID as LD_PID,
} from "@helium/lazy-distributor-sdk";
import {
init as initRewards,
PROGRAM_ID as RO_PID,
} from "@helium/rewards-oracle-sdk";
import {
Asset,
getAsset,
HNT_MINT,
IOT_MINT,
toNumber,
} from "@helium/spl-utils";
import { AccountFetchCache } from "@helium/account-fetch-cache";
import {
Keypair,
PublicKey,
Transaction,
TransactionInstruction
TransactionInstruction,
} from "@solana/web3.js";
import { Op } from "sequelize";
import fs from "fs";
import { Reward } from "./model";
import { Reward, sequelize } from "./model";
import Fastify, {
FastifyInstance,
FastifyRequest,
Expand All @@ -44,19 +55,25 @@ import Fastify, {
import cors from "@fastify/cors";
import { getLeafAssetId } from "@metaplex-foundation/mpl-bubblegum";
import { RewardsOracle } from "@helium/idls/lib/types/rewards_oracle";

const HNT = process.env.HNT_MINT ? new PublicKey(process.env.HNT_MINT) : HNT_MINT;
import { register, totalRewardsGauge } from "./metrics";

const HNT = process.env.HNT_MINT
? new PublicKey(process.env.HNT_MINT)
: HNT_MINT;
const DNT = process.env.DNT_MINT
? new PublicKey(process.env.DNT_MINT)
: IOT_MINT;
const DAO = daoKey(HNT)[0];
const ENTITY_CREATOR = entityCreatorKey(DAO)[0];

export interface Database {
getTotalRewards(): Promise<string>;
getCurrentRewardsByEntity: (entityKey: string) => Promise<string>;
getCurrentRewards: (asset: PublicKey) => Promise<string>;
getBulkRewards: (entityKeys: string[]) => Promise<Record<string, string>>;
getActiveDevices(): Promise<number>;
}


export class PgDatabase implements Database {
constructor(
readonly issuanceProgram: Program<HeliumEntityManager>,
Expand All @@ -66,6 +83,17 @@ export class PgDatabase implements Database {
) => Promise<Asset | undefined> = getAsset
) {}

async getTotalRewards(): Promise<string> {
const totalRewards = (
await Reward.findAll({
attributes: [
[sequelize.fn("SUM", sequelize.col("rewards")), "rewards"],
],
})
)[0].rewards;
return totalRewards;
}

getActiveDevices(): Promise<number> {
return Reward.count({
where: {
Expand Down Expand Up @@ -167,6 +195,24 @@ export class OracleServer {
server.get("/health", async () => {
return { ok: true };
});
server.register(fastifyCron, {
jobs: [
{
cronTime: "0 * * * *",
runOnInit: true,
onTick: async () => {
console.log("Updating total rewards");
const rewards = toNumber(new BN(await db.getTotalRewards()), 6);
totalRewardsGauge
.labels(DNT.toBase58())
.set(Number(rewards));
},
},
],
});
server.get("/metrics", async (request, reply) => {
return register.metrics();
});

this.app = server;
this.addRoutes();
Expand All @@ -187,10 +233,13 @@ export class OracleServer {
private addRoutes() {
this.app.get("/active-devices", this.getActiveDevicesHandler.bind(this));
this.app.post("/bulk-rewards", this.getAllRewardsHandler.bind(this));
this.app.get<{ Querystring: { assetId?: string; entityKey?: string, keySerialization?: BufferEncoding | "b58" } }>(
"/",
this.getCurrentRewardsHandler.bind(this)
);
this.app.get<{
Querystring: {
assetId?: string;
entityKey?: string;
keySerialization?: BufferEncoding | "b58";
};
}>("/", this.getCurrentRewardsHandler.bind(this));
this.app.post("/", this.signTransactionHandler.bind(this));
this.app.post("/bulk-sign", this.signBulkTransactionsHandler.bind(this));
}
Expand Down Expand Up @@ -232,8 +281,12 @@ export class OracleServer {
}

if (entityKey) {
const [key] = await keyToAssetKey(this.dao, entityKey as string, keySerialization);
console.log(key.toBase58())
const [key] = await keyToAssetKey(
this.dao,
entityKey as string,
keySerialization
);
console.log(key.toBase58());
const keyToAsset = await this.hemProgram.account.keyToAssetV0.fetch(key);
assetId = keyToAsset.asset.toBase58();
}
Expand Down Expand Up @@ -432,10 +485,15 @@ export class OracleServer {
keyToAssetK = ix.keys[wrapperKeyToAssetIdx].pubkey;
//@ts-ignore
proposedCurrentRewards = decoded.data.args.currentRewards;
entityKey = (await this.hemProgram.account.keyToAssetV0.fetch(keyToAssetK)).entityKey;
entityKey = (
await this.hemProgram.account.keyToAssetV0.fetch(keyToAssetK)
).entityKey;
// A sneaky RPC could return incorrect data. Verify that the entity key is correct for the key to asset
if (!keyToAssetKey(this.dao, entityKey)[0].equals(keyToAssetK)) {
return { success: false, message: "RPC lied about the entity key for this asset." };
return {
success: false,
message: "RPC lied about the entity key for this asset.",
};
}
} else if (
ix.keys[oracleKeyIdx].pubkey.equals(this.oracle.publicKey) &&
Expand Down Expand Up @@ -467,14 +525,13 @@ export class OracleServer {
}
mint = recipientAcc.asset;
}
let keySerialization: any = { b58: {} }
let keySerialization: any = { b58: {} };
if (keyToAssetK) {
const keyToAsset = await this.hemProgram.account.keyToAssetV0.fetch(
keyToAssetK
);
keySerialization = keyToAsset.keySerialization;
}


// @ts-ignore
const currentRewards = entityKey
Expand All @@ -483,7 +540,10 @@ export class OracleServer {
)
: await this.db.getCurrentRewards(mint);
if (proposedCurrentRewards.gt(new BN(currentRewards))) {
return { success: false, message: `Invalid amount, ${proposedCurrentRewards} is greater than actual rewards ${currentRewards}` };
return {
success: false,
message: `Invalid amount, ${proposedCurrentRewards} is greater than actual rewards ${currentRewards}`,
};
}
}

Expand Down Expand Up @@ -527,13 +587,11 @@ export class OracleServer {

const errIdx = results.findIndex((x) => !x.success);
if (errIdx > -1) {
res
.status(400)
.send({
error: results[errIdx].message
? `${results[errIdx].message}\n\nTransaction index: ${errIdx}`
: `Error signing transaction index: ${errIdx}`,
});
res.status(400).send({
error: results[errIdx].message
? `${results[errIdx].message}\n\nTransaction index: ${errIdx}`
: `Error signing transaction index: ${errIdx}`,
});
return;
}

Expand Down Expand Up @@ -583,17 +641,15 @@ export class OracleServer {
const ldProgram = await initLazy(provider);
const roProgram = await initRewards(provider);
const hemProgram = await initHeliumEntityManager(provider);
const DNT = process.env.DNT_MINT
? new PublicKey(process.env.DNT_MINT)
: IOT_MINT;

const LAZY_DISTRIBUTOR = lazyDistributorKey(DNT)[0];
const server = new OracleServer(
ldProgram,
roProgram,
hemProgram,
oracleKeypair,
new PgDatabase(hemProgram),
LAZY_DISTRIBUTOR,
LAZY_DISTRIBUTOR
);
// For performance
new AccountFetchCache({
Expand Down
Loading

0 comments on commit dbd2f96

Please sign in to comment.