Skip to content

Commit

Permalink
Add investor counts endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
prevostc committed Nov 27, 2023
1 parent 64adaa8 commit 2300fab
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 9 deletions.
1 change: 0 additions & 1 deletion deploy/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ version: "3.9" # optional since v1.27.0
services:
redis:
image: redis:7-alpine
container_name: redis-dev
command: redis-server --save "" --appendonly no --maxmemory 50mb --replicaof no one
ports:
- "6379:6379"
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
"scripts": {
"test": "LOG_LEVEL=error TZ=UTC jest --maxConcurrency=1",
"build": "tsc",
"infra:start": "cd ./deploy && docker compose -p dev -f docker-compose.dev.yml up -d",
"infra:logs": "cd ./deploy && docker compose -p dev -f docker-compose.dev.yml logs -f",
"infra:stop": "cd ./deploy && docker compose -p dev -f docker-compose.dev.yml down",
"infra:start": "cd ./deploy && docker compose -p dev-databarn -f docker-compose.dev.yml up -d",
"infra:logs": "cd ./deploy && docker compose -p dev-databarn -f docker-compose.dev.yml logs -f",
"infra:stop": "cd ./deploy && docker compose -p dev-databarn -f docker-compose.dev.yml down",
"grafana:open": "open http://localhost:3000",
"pg:console": "docker exec -it dev-timescaledb-1 psql -U beefy",
"pg:console": "docker exec -it dev-databarn-timescaledb-1 psql -U beefy",
"db:migrate": "ts-node ./src/script/run.ts db:migrate",
"redis:console": "docker exec -it redis-dev redis-cli",
"redis:clear": "docker exec -it redis-dev redis-cli flushall",
"redis:console": "docker exec -it dev-databarn-redis-1 redis-cli",
"redis:clear": "docker exec -it dev-databarn-redis-1 redis-cli flushall",
"test:outdated": "ncu",
"test:unused-exports": "ts-unused-exports ./tsconfig.json",
"test:all": "npm run build && npm run test && npm run test:unused-exports",
Expand Down
3 changes: 2 additions & 1 deletion src/api/route/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export default async function (instance: FastifyInstance) {
ChainEnum: { $id: "ChainEnum", type: "string", enum: allChainIds, description: "The chain identifier" },
},
ProductService.schemaComponents,
BeefyPortfolioService.schemaComponents,
BeefyPortfolioService.timelineSchemaComponents,
BeefyPortfolioService.investorCountsSchemaComponents,
);

for (const component of Object.values(mergedComponents)) {
Expand Down
17 changes: 17 additions & 0 deletions src/api/route/public/protocol/beefy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ export default async function (instance: FastifyInstance, opts: FastifyPluginOpt
});
}

{
const schema = {
tags: ["stats"],
summary: "Get investor counts",
description: "Returns the number of investors for each chain",
response: {
200: BeefyPortfolioService.investorCountsSchema,
},
};
type TRoute = {};

instance.get<TRoute>("/investor-counts", merge({}, opts.routeOpts, { schema }), async (req, reply) => {
const investorCounts = await instance.diContainer.cradle.beefy.getInvestorCounts();
return reply.send(investorCounts);
});
}

{
const schema = {
params: S.object().prop("chain", chainSchema.required().description("Only include produce for this chain")),
Expand Down
142 changes: 141 additions & 1 deletion src/api/service/protocol/beefy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ProductService } from "../product";
export class BeefyPortfolioService {
constructor(private services: { db: DbClient; cache: AsyncCache; product: ProductService; price: PriceService }) {}

public static schemaComponents = {
public static timelineSchemaComponents = {
InvestorTimelineRow: {
$id: "InvestorTimelineRow",
type: "object",
Expand Down Expand Up @@ -107,4 +107,144 @@ export class BeefyPortfolioService {
);
});
}

public static investorCountsSchemaComponents = {
InvestorCountResult: {
$id: "InvestorCountResult",
type: "object",
properties: {
investor_count_columns: {
type: "array",
items: { type: "string" },
description: "The columns for the investor counts as the actual data is contained in an array to reduce the size of the response",
example: ["investor_count", "investor_count_more_than_1_usd", "investor_count_more_than_10_usd"],
},
items: {
type: "array",
items: { $ref: "InvestorCountItem" },
description: "The actual investor counts for each product",
},
},
required: ["investor_count_columns", "items"],
},

InvestorCountItem: {
$id: "InvestorCountItem",
type: "object",
properties: {
product_key: { type: "string", description: "The product key", example: productKeyExamples[0] },
beefy_id: { type: "string", description: "The beefy vault id", example: "cake-bnb" },
investor_counts: {
type: "array",
items: { type: "number" },
description:
"Investor counts, each value corresponds to the column names in investor_count_columns. The first value is the total investor count, the second value is the investor count with a balance > 1 USD, the third value is the investor count with a balance > 10 USD, etc.",
example: [123, 12, 1],
},
},
required: ["product_key", "beefy_id", "investor_counts"],
},
};

public static investorCountsSchema = {
description:
"Count for all beefy products, including the number of investors with a balance > 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000",
$ref: "InvestorCountResult",
};

async getInvestorCounts() {
const cacheKey = `api:portfolio-service:investor-count`;
const ttl = 1000 * 60 * 60 * 24; // 1 day cache
return this.services.cache.wrap(cacheKey, ttl, async () => {
const results = await db_query<{
product_key: string;
beefy_id: string;
investor_count: number;
investor_count_more_than_1_usd: number;
investor_count_more_than_10_usd: number;
investor_count_more_than_100_usd: number;
investor_count_more_than_1000_usd: number;
investor_count_more_than_10000_usd: number;
investor_count_more_than_100000_usd: number;
investor_count_more_than_1000000_usd: number;
investor_count_more_than_10000000_usd: number;
}>(
`
with last_balance as (
select
product_id,
investor_id,
last(usd_balance, datetime) as last_usd_balance
from beefy_investor_timeline_cache_ts
where product_id IN (
select product_id
from product
where coalesce(product_data->>'dashboardEol')::text = 'false'
)
group by 1,2
),
investor_count_by_product_id as (
select
product_id,
count(*) as investor_count,
count(*) filter(where last_usd_balance > 1) as investor_count_more_than_1_usd,
count(*) filter(where last_usd_balance > 10) as investor_count_more_than_10_usd,
count(*) filter(where last_usd_balance > 100) as investor_count_more_than_100_usd,
count(*) filter(where last_usd_balance > 1000) as investor_count_more_than_1000_usd,
count(*) filter(where last_usd_balance > 10000) as investor_count_more_than_10000_usd,
count(*) filter(where last_usd_balance > 100000) as investor_count_more_than_100000_usd,
count(*) filter(where last_usd_balance > 1000000) as investor_count_more_than_1000000_usd,
count(*) filter(where last_usd_balance > 10000000) as investor_count_more_than_10000000_usd
from last_balance
group by 1
)
select
p.product_key,
coalesce(p.product_data->'vault'->>'id', p.product_data->'boost'->>'id') as beefy_id,
c.investor_count,
c.investor_count_more_than_1_usd,
c.investor_count_more_than_10_usd,
c.investor_count_more_than_100_usd,
c.investor_count_more_than_1000_usd,
c.investor_count_more_than_10000_usd,
c.investor_count_more_than_100000_usd,
c.investor_count_more_than_1000000_usd,
c.investor_count_more_than_10000000_usd
from investor_count_by_product_id c
join product p using(product_id)
`,
[],
this.services.db,
);

return {
investor_count_columns: [
"investor_count",
"investor_count_more_than_1_usd",
"investor_count_more_than_10_usd",
"investor_count_more_than_100_usd",
"investor_count_more_than_1000_usd",
"investor_count_more_than_10000_usd",
"investor_count_more_than_100000_usd",
"investor_count_more_than_1000000_usd",
"investor_count_more_than_10000000_usd",
],
items: results.map((row) => ({
product_key: row.product_key,
beefy_id: row.beefy_id,
investor_counts: [
row.investor_count,
row.investor_count_more_than_1_usd,
row.investor_count_more_than_10_usd,
row.investor_count_more_than_100_usd,
row.investor_count_more_than_1000_usd,
row.investor_count_more_than_10000_usd,
row.investor_count_more_than_100000_usd,
row.investor_count_more_than_1000000_usd,
row.investor_count_more_than_10000000_usd,
],
})),
};
});
}
}

0 comments on commit 2300fab

Please sign in to comment.