Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/api/bigDipperApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ export class BigDipperApi {
constructor(public readonly graphql_client: GraphQLClient) {}

async getTotalSupply(): Promise<number> {
let query = `query TotalSupply {
const query = `query TotalSupply {
supply {
coins
}
}`;

let resp = await this.graphql_client.query<{
const resp = await this.graphql_client.query<{
data: TotalSupplyResponse;
}>(query);

return Number(resp.data.supply[0].coins.find((coin) => coin.denom === 'ncheq')?.amount || '0');
}

getTotalStakedCoins = async (): Promise<string> => {
let query = `query StakingInfo{
const query = `query StakingInfo{
staking_pool {
bonded_tokens
}
Expand Down
12 changes: 6 additions & 6 deletions src/api/nodeApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ export class NodeApi {
constructor(public readonly base_rest_api_url: string) {}

async getAccountInfo(address: string): Promise<Account> {
let resp = await fetch(`${this.base_rest_api_url}/cosmos/auth/v1beta1/accounts/${address}`);
let respJson = (await resp.json()) as { account: Account };
const resp = await fetch(`${this.base_rest_api_url}/cosmos/auth/v1beta1/accounts/${address}`);
const respJson = (await resp.json()) as { account: Account };

return respJson.account;
}

async getAvailableBalance(address: string): Promise<Coin[]> {
let resp = await fetch(`${this.base_rest_api_url}/cosmos/bank/v1beta1/balances/${address}`);
let respJson = (await resp.json()) as { balances: Coin[] };
const resp = await fetch(`${this.base_rest_api_url}/cosmos/bank/v1beta1/balances/${address}`);
const respJson = (await resp.json()) as { balances: Coin[] };

return respJson.balances;
}

async distributionGetRewards(address: string): Promise<number> {
let resp = await fetch(`${this.base_rest_api_url}/cosmos/distribution/v1beta1/delegators/${address}/rewards`);
let respJson = (await resp.json()) as RewardsResponse;
const resp = await fetch(`${this.base_rest_api_url}/cosmos/distribution/v1beta1/delegators/${address}/rewards`);
const respJson = (await resp.json()) as RewardsResponse;

return Number(respJson?.total?.[0]?.amount ?? '0');
}
Expand Down
2 changes: 1 addition & 1 deletion src/database/scripts/initialDataFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ clientConfig.ssl = {
ca: fs.readFileSync('/tmp/do-cert.pem').toString(),
};

let client = new Client(clientConfig);
const client = new Client(clientConfig);

client.connect();
const db = drizzle(client, { logger: false });
Expand Down
2 changes: 1 addition & 1 deletion src/database/scripts/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ clientConfig.ssl = {
ca: fs.readFileSync('/tmp/do-cert.pem').toString(),
};

let client = new Client(clientConfig);
const client = new Client(clientConfig);

client.connect();
const db = drizzle(client, { logger: true });
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function parseQueryParams(url: URL): AnalyticsQueryParams {
export async function handler(
request: IRequest,
env: Env,
ctx: ExecutionContext,
_ctx: ExecutionContext,
network: Network,
entityType?: EntityType
): Promise<Response> {
Expand Down
3 changes: 1 addition & 2 deletions src/handlers/circulatingSupply.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { IRequest } from 'itty-router';
import { getCirculatingSupply } from '../helpers/circulating';

export async function handler(request: IRequest, env: Env): Promise<Response> {
export async function handler(env: Env): Promise<Response> {
try {
const circulating_supply = await getCirculatingSupply(env);
return new Response(circulating_supply.toString());
Expand Down
16 changes: 8 additions & 8 deletions src/handlers/liquidBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,34 @@ export async function handler(request: IRequest, env: Env): Promise<Response> {
throw new Error('No address specified or wrong address format.');
}

let api = new NodeApi(env.REST_API);
const api = new NodeApi(env.REST_API);
const account = await api.getAccountInfo(address);

if (!isVestingAccount(account['@type'])) {
throw new Error(`Only vesting accounts are supported. Accounts type '${account['@type']}'.`);
}

if (isDelayedVestingAccount(account?.['@type'])) {
let balance =
const balance =
account?.base_vesting_account?.base_account?.sequence !== '0'
? Number(
(await (await api.getAvailableBalance(address)).find((b) => b.denom === 'ncheq')?.amount) ?? '0'
)
: 0;
let rewards = Number((await await api.distributionGetRewards(address)) ?? '0');
let delegated = Number(
const rewards = Number((await await api.distributionGetRewards(address)) ?? '0');
const delegated = Number(
account?.base_vesting_account?.delegated_free?.find((d) => d.denom === 'ncheq')?.amount ?? '0'
);

return new Response(convertToMainTokenDenom(balance + rewards + delegated, env.TOKEN_EXPONENT));
}

let vested_coins = Number(calculateVesting(account)?.vested);
let balance = Number(
const vested_coins = Number(calculateVesting(account)?.vested);
const balance = Number(
(await (await api.getAvailableBalance(address)).find((b) => b.denom === 'ncheq')?.amount) ?? '0'
);
let rewards = Number((await api.distributionGetRewards(address)) ?? '0');
let liquid_coins = vested_coins + balance + rewards;
const rewards = Number((await api.distributionGetRewards(address)) ?? '0');
const liquid_coins = vested_coins + balance + rewards;

return new Response(convertToMainTokenDenom(liquid_coins, env.TOKEN_EXPONENT));
}
2 changes: 1 addition & 1 deletion src/handlers/totalBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { fetchAccountBalances } from '../helpers/balance';

export async function handler(request: IRequest, env: Env): Promise<Response> {
const address = request.params?.['address'];
let account_balance_infos = await fetchAccountBalances(address!!, env);
const account_balance_infos = await fetchAccountBalances(address!, env);
return new Response(account_balance_infos?.totalBalance.toString());
}
9 changes: 4 additions & 5 deletions src/handlers/totalStakedCoins.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { IRequest } from 'itty-router';
import { BigDipperApi } from '../api/bigDipperApi';
import { convertToMainTokenDenom } from '../helpers/currency';
import { GraphQLClient } from '../helpers/graphql';

export async function handler(request: IRequest, env: Env): Promise<Response> {
let gql_client = new GraphQLClient(env.GRAPHQL_API);
let bd_api = new BigDipperApi(gql_client);
export async function handler(env: Env): Promise<Response> {
const gql_client = new GraphQLClient(env.GRAPHQL_API);
const bd_api = new BigDipperApi(gql_client);

let total_staked_coins = await bd_api.getTotalStakedCoins();
const total_staked_coins = await bd_api.getTotalStakedCoins();

return new Response(convertToMainTokenDenom(Number(total_staked_coins), env.TOKEN_EXPONENT));
}
7 changes: 3 additions & 4 deletions src/handlers/totalSupply.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { IRequest } from 'itty-router';
import { BigDipperApi } from '../api/bigDipperApi';
import { convertToMainTokenDenom } from '../helpers/currency';
import { GraphQLClient } from '../helpers/graphql';

export async function handler(request: IRequest, env: Env): Promise<Response> {
let gql_client = new GraphQLClient(env.GRAPHQL_API);
let bd_api = new BigDipperApi(gql_client);
export async function handler(env: Env): Promise<Response> {
const gql_client = new GraphQLClient(env.GRAPHQL_API);
const bd_api = new BigDipperApi(gql_client);
const total_supply = await bd_api.getTotalSupply();
return new Response(convertToMainTokenDenom(total_supply, env.TOKEN_EXPONENT));
}
6 changes: 3 additions & 3 deletions src/handlers/vestedBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ export async function handler(request: IRequest, env: Env): Promise<Response> {
throw new Error('No address specified or wrong address format.');
}

let api = new NodeApi(env.REST_API);
const api = new NodeApi(env.REST_API);
const account = await api.getAccountInfo(address);

if (!isVestingAccount(account['@type'])) {
throw new Error(`Only vesting accounts are supported. Accounts type '${account['@type']}'.`);
}

let vested_coins = calculateVesting(account)?.vested;
const vested_coins = calculateVesting(account)?.vested;

return new Response(convertToMainTokenDenom(vested_coins!!, env.TOKEN_EXPONENT));
return new Response(convertToMainTokenDenom(vested_coins!, env.TOKEN_EXPONENT));
}
6 changes: 3 additions & 3 deletions src/handlers/vestingBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ export async function handler(request: IRequest, env: Env): Promise<Response> {
throw new Error('No address specified or wrong address format.');
}

let api = new NodeApi(env.REST_API);
const api = new NodeApi(env.REST_API);
const account = await api.getAccountInfo(address);

if (!isVestingAccount(account['@type'])) {
throw new Error(`Only vesting accounts are supported. Accounts type '${account['@type']}'.`);
}

let vestingCoins = calculateVesting(account)?.vesting;
return new Response(convertToMainTokenDenom(vestingCoins!!, env.TOKEN_EXPONENT));
const vestingCoins = calculateVesting(account)?.vesting;
return new Response(convertToMainTokenDenom(vestingCoins!, env.TOKEN_EXPONENT));
}
8 changes: 1 addition & 7 deletions src/handlers/webhookTriggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ export async function syncIdentityData(env: Env) {

function getHour(): number {
// This function only works when CIRCULATING_SUPPLY_GROUPS is set to 24
let hour = Number(new Date().getHours() + 1); // getHours() returns 0-23
const hour = Number(new Date().getHours() + 1); // getHours() returns 0-23
return hour;
}

function getRandomGroup(group: number): number {
let min = 1;
let max = Math.floor(group);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
4 changes: 2 additions & 2 deletions src/helpers/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
denomMainnet,
denomTestnet,
} from '../database/schema';
import { AnalyticsQueryParams, AnalyticsResponse } from '../types/analytics';
import { AnalyticsQueryParams, AnalyticsResponse, AnalyticsItem } from '../types/analytics';
import { DrizzleClient } from '../database/client';
import { serializeBigInt } from './csv';
import { Network } from '../types/network';
Expand Down Expand Up @@ -74,7 +74,7 @@ export function buildQueryConditions(
}

// Format response
function formatResponse(items: any[], totalCount: number, params: AnalyticsQueryParams): AnalyticsResponse {
function formatResponse(items: AnalyticsItem[], totalCount: number, params: AnalyticsQueryParams): AnalyticsResponse {
return {
items: serializeBigInt(items),
totalCount,
Expand Down
29 changes: 16 additions & 13 deletions src/helpers/circulating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export async function updateCirculatingSupply(groupNumber: number, env: Env) {

for (const key of cached.keys) {
const parts = extractPrefixAndKey(key.name);
let addr = parts.address;
let grpN = parts.groupNumber;
const addr = parts.address;
const grpN = parts.groupNumber;

const found = await env.CIRCULATING_SUPPLY_WATCHLIST.get(`group_${grpN}:${addr}`);
if (found) {
Expand All @@ -29,8 +29,9 @@ export async function updateCirculatingSupply(groupNumber: number, env: Env) {
}
}
}
} catch (e) {
console.log('Error at: ', 'updateCirculatingSupply');
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.log(`Error updating circulating supply for group ${groupNumber}: ${errorMessage}`);
}
}

Expand All @@ -43,23 +44,24 @@ export async function updateCachedBalance(addr: string, grpN: number, env: Env)
await env.CIRCULATING_SUPPLY_WATCHLIST.put(`group_${grpN}:${addr}`, data);

console.log(`account "${addr}" balance updated. (${data})`);
} catch (e: any) {
console.log(`error updateCachedBalance: ${e}`);
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.log(`Error updating cached balance for address ${addr}: ${errorMessage}`);
}
}

export async function getCirculatingSupply(env: Env): Promise<number> {
let gql_client = new GraphQLClient(env.GRAPHQL_API);
let bd_api = new BigDipperApi(gql_client);
let total_supply_ncheq = await bd_api.getTotalSupply();
const gql_client = new GraphQLClient(env.GRAPHQL_API);
const bd_api = new BigDipperApi(gql_client);
const total_supply_ncheq = await bd_api.getTotalSupply();
const total_supply = Number(convertToMainTokenDenom(total_supply_ncheq, env.TOKEN_EXPONENT));

try {
const cached = await env.CIRCULATING_SUPPLY_WATCHLIST.list();
console.log(`Total cached entries: ${cached.keys.length}`);
let shareholders_total_balance = Number(0);
for (const key of cached.keys) {
let data: AccountBalanceInfos | null = await env.CIRCULATING_SUPPLY_WATCHLIST.get(key.name, {
const data: AccountBalanceInfos | null = await env.CIRCULATING_SUPPLY_WATCHLIST.get(key.name, {
type: 'json',
});

Expand All @@ -71,9 +73,10 @@ export async function getCirculatingSupply(env: Env): Promise<number> {
console.log('Total supply', total_supply);
console.log(`Watchlist total balance: ${shareholders_total_balance}`);

let circulating_supply = total_supply - shareholders_total_balance;
const circulating_supply = total_supply - shareholders_total_balance;
return circulating_supply;
} catch (e: any) {
throw new Error(e.toString);
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to calculate circulating supply: ${errorMessage}`);
}
}
20 changes: 12 additions & 8 deletions src/helpers/csv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getTables, buildQueryConditions } from './analytics';
import { eq, and, desc, sql } from 'drizzle-orm';

// Convert data to CSV format
export function convertToCSV(data: any[]): string {
export function convertToCSV(data: Record<string, unknown>[]): string {
if (!data || !data.length) {
return 'No data available';
}
Expand Down Expand Up @@ -92,7 +92,7 @@ export function generateExportFilename(
.substring(0, 255); // Limit filename length
}

export function serializeBigInt(data: any): any {
export function serializeBigInt<T>(data: T): T {
return JSON.parse(JSON.stringify(data, (_, value) => (typeof value === 'bigint' ? value.toString() : value)));
}

Expand Down Expand Up @@ -145,14 +145,16 @@ export async function exportResourceAnalytics(
// Execute query without pagination
const items = await db
.select({
didId: tables.resource.didId, // Now first
resourceId: tables.resource.resourceId,
resourceType: tables.resource.resourceType,
resourceName: tables.resource.resourceName,
operationType: tables.operationTypes.friendlyOperationType,
didId: tables.resource.didId,
ledgerOperationType: tables.operationTypes.ledgerOperationType,
feePayer: tables.resource.feePayer,
amount: sql`${tables.resource.amount}::decimal / POW(10, ${tables.denom.exponent})`,
denom: tables.denom.friendlyDenom,
ledgerDenom: tables.denom.ledgerDenom,
blockHeight: tables.resource.blockHeight,
transactionHash: tables.resource.transactionHash,
createdAt: tables.resource.createdAt,
Expand Down Expand Up @@ -184,16 +186,17 @@ export async function exportAllAnalytics(
// Fetch DIDs
const didItems = await db
.select({
type: sql`'DID'`.as('type'), // Add a type column to distinguish records
id: tables.did.didId,
type: sql`'DID'`.as('type'),
didId: tables.did.didId,
resourceId: sql`NULL`.as('resourceId'),
resourceType: sql`NULL`.as('resourceType'),
resourceName: sql`NULL`.as('resourceName'),
operationType: tables.operationTypes.friendlyOperationType,
ledgerOperationType: tables.operationTypes.ledgerOperationType,
feePayer: tables.did.feePayer,
amount: sql`${tables.did.amount}::decimal / POW(10, ${tables.denom.exponent})`,
denom: tables.denom.friendlyDenom,
ledgerDenom: tables.denom.ledgerDenom,
blockHeight: tables.did.blockHeight,
transactionHash: tables.did.transactionHash,
createdAt: tables.did.createdAt,
Expand All @@ -205,19 +208,20 @@ export async function exportAllAnalytics(
.where(and(...didConditions))
.orderBy(desc(tables.did.createdAt));

// Fetch Resources
// Fetch Resources with the same structure as DIDs for consistent headers
const resourceItems = await db
.select({
type: sql`'Resource'`.as('type'), // Add a type column to distinguish records
id: tables.resource.resourceId,
type: sql`'Resource'`.as('type'),
didId: tables.resource.didId,
resourceId: tables.resource.resourceId,
resourceType: tables.resource.resourceType,
resourceName: tables.resource.resourceName,
operationType: tables.operationTypes.friendlyOperationType,
ledgerOperationType: tables.operationTypes.ledgerOperationType,
feePayer: tables.resource.feePayer,
amount: sql`${tables.resource.amount}::decimal / POW(10, ${tables.denom.exponent})`,
denom: tables.denom.friendlyDenom,
ledgerDenom: tables.denom.ledgerDenom,
blockHeight: tables.resource.blockHeight,
transactionHash: tables.resource.transactionHash,
createdAt: tables.resource.createdAt,
Expand Down
Loading