Skip to content

Commit

Permalink
Functional endpoints (adapted from ERC20 API)
Browse files Browse the repository at this point in the history
  • Loading branch information
0237h committed Dec 12, 2023
1 parent ae257f3 commit 2ec04e9
Show file tree
Hide file tree
Showing 19 changed files with 755 additions and 207 deletions.
Binary file added buffer.db
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "antelope-token-api",
"description": "Token prices from the Antelope blockchains",
"description": "Transfers and account balances from the Antelope blockchains",
"version": "0.1.0",
"homepage": "https://github.com/pinax-network/antelope-token-api",
"license": "MIT",
Expand Down
1 change: 1 addition & 0 deletions src/clickhouse/createClient.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// from: https://github.com/pinax-network/substreams-clock-api/blob/main/src/clickhouse/createClient.ts
import { createClient } from "@clickhouse/client-web";
import { ping } from "./ping.js";
import { APP_NAME, config } from "../config.js";
Expand Down
26 changes: 17 additions & 9 deletions src/clickhouse/makeQuery.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// from: https://github.com/pinax-network/substreams-clock-api/blob/main/src/clickhouse/makeQuery.ts
import { logger } from "../logger.js";
import * as prometheus from "../prometheus.js";
import client from "./createClient.js";
Expand All @@ -18,12 +19,19 @@ export interface Query<T> {
}

export async function makeQuery<T = unknown>(query: string) {
const response = await client.query({ query })
const data: Query<T> = await response.json();
prometheus.query.inc();
prometheus.bytes_read.inc(data.statistics.bytes_read);
prometheus.rows_read.inc(data.statistics.rows_read);
prometheus.elapsed.inc(data.statistics.elapsed);
logger.info({ query, statistics: data.statistics, rows: data.rows });
return data;
}
try {
const response = await client.query({ query })
const data: Query<T> = await response.json();
prometheus.query.inc();
prometheus.bytes_read.inc(data.statistics.bytes_read);
prometheus.rows_read.inc(data.statistics.rows_read);
prometheus.elapsed.inc(data.statistics.elapsed);
logger.info({ query, statistics: data.statistics, rows: data.rows });
return data;
} catch (e: any) {

console.error(e.message)
return { data: [] }
}

}
1 change: 1 addition & 0 deletions src/clickhouse/ping.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// from: https://github.com/pinax-network/substreams-clock-api/blob/main/src/clickhouse/ping.ts
import { PingResult } from "@clickhouse/client-web";
import client from "./createClient.js";

Expand Down
9 changes: 3 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ export const DEFAULT_PORT = "8080";
export const DEFAULT_HOSTNAME = "localhost";
export const DEFAULT_HOST = "http://localhost:8123";
export const DEFAULT_DATABASE = "default";
export const DEFAULT_TABLE = "blocks";
export const DEFAULT_USERNAME = "default";
export const DEFAULT_PASSWORD = "";
export const DEFAULT_MAX_LIMIT = 500;
export const DEFAULT_MAX_LIMIT = 10000;
export const DEFAULT_VERBOSE = false;
export const APP_NAME = pkg.name;
export const DEFAULT_SORT_BY = "DESC";
export const APP_NAME = pkg.name;

// parse command line options
const opts = program
Expand All @@ -29,7 +28,6 @@ const opts = program
.addOption(new Option("--username <string>", "Database user").env("USERNAME").default(DEFAULT_USERNAME))
.addOption(new Option("--password <string>", "Password associated with the specified username").env("PASSWORD").default(DEFAULT_PASSWORD))
.addOption(new Option("--database <string>", "The database to use inside ClickHouse").env("DATABASE").default(DEFAULT_DATABASE))
.addOption(new Option("--table <string>", "Clickhouse table name").env("TABLE").default(DEFAULT_TABLE))
.addOption(new Option("--max-limit <number>", "Maximum LIMIT queries").env("MAX_LIMIT").default(DEFAULT_MAX_LIMIT))
.parse()
.opts();
Expand All @@ -38,10 +36,9 @@ export const config = z.object({
port: z.string(),
hostname: z.string(),
host: z.string(),
table: z.string(),
database: z.string(),
username: z.string(),
password: z.string(),
maxLimit: z.coerce.number(),
verbose: z.coerce.boolean(),
}).parse(opts);
}).parse(opts);
28 changes: 16 additions & 12 deletions src/fetch/GET.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@ import { registry } from "../prometheus.js";
import openapi from "./openapi.js";
import health from "./health.js";
import chains from "./chains.js";
import block from "./block.js";
import balance from "./balance.js";
import supply from "./supply.js";
import * as prometheus from "../prometheus.js";
import { logger } from "../logger.js";
import swaggerHtml from "../../swagger/index.html"
import swaggerFavicon from "../../swagger/favicon.png"
import transfers from "./transfers.js";

export default async function (req: Request) {
const { pathname} = new URL(req.url);
prometheus.request.inc({pathname});
if ( pathname === "/" ) return new Response(Bun.file(swaggerHtml));
if ( pathname === "/favicon.png" ) return new Response(Bun.file(swaggerFavicon));
if ( pathname === "/health" ) return health(req);
if ( pathname === "/metrics" ) return new Response(await registry.metrics(), {headers: {"Content-Type": registry.contentType}});
if ( pathname === "/openapi" ) return new Response(openapi, {headers: {"Content-Type": "application/json"}});
if ( pathname === "/chains" ) return chains(req);
//if ( pathname === "/block" ) return block(req);
const { pathname } = new URL(req.url);
prometheus.request.inc({ pathname });
if (pathname === "/") return new Response(Bun.file(swaggerHtml));
if (pathname === "/favicon.png") return new Response(Bun.file(swaggerFavicon));
if (pathname === "/health") return health(req);
if (pathname === "/metrics") return new Response(await registry.metrics(), { headers: { "Content-Type": registry.contentType } });
if (pathname === "/openapi") return new Response(openapi, { headers: { "Content-Type": "application/json" } });
if (pathname === "/chains") return chains(req);
if (pathname === "/supply") return supply(req);
if (pathname === "/balance") return balance(req);
if (pathname === "/transfers") return transfers(req);
logger.warn(`Not found: ${pathname}`);
prometheus.request_error.inc({pathname, status: 404});
prometheus.request_error.inc({ pathname, status: 404 });
return new Response("Not found", { status: 404 });
}
}
32 changes: 32 additions & 0 deletions src/fetch/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// from: https://github.com/pinax-network/substreams-clock-api/blob/main/src/fetch/block.ts
import { makeQuery } from "../clickhouse/makeQuery.js";
import { logger } from "../logger.js";
import { getBalanceChanges } from "../queries.js";
import * as prometheus from "../prometheus.js";
import { toJSON } from "./utils.js";


function verifyParams(searchParams: URLSearchParams) {
const chain = searchParams.get("chain");
const owner = searchParams.get("owner");
const contract = searchParams.get("contract");

if (!chain) throw new Error("chain is required");
if (!owner && !contract) throw new Error("owner or contract is required");
}

export default async function (req: Request) {
try {
const { searchParams } = new URL(req.url);
logger.info({ searchParams: Object.fromEntries(Array.from(searchParams)) });
//Verify required params
verifyParams(searchParams);
const query = getBalanceChanges(searchParams);
const response = await makeQuery(query)
return toJSON(response.data);
} catch (e: any) {
logger.error(e);
prometheus.request_error.inc({ pathname: "/balance", status: 400 });
return new Response(e.message, { status: 400 });
}
}
10 changes: 6 additions & 4 deletions src/fetch/chains.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
// from: https://github.com/pinax-network/substreams-clock-api/blob/main/src/fetch/chains.ts
import { makeQuery } from "../clickhouse/makeQuery.js";
import { logger } from "../logger.js";
import * as prometheus from "../prometheus.js";
import { getChain } from "../queries.js";
import { toJSON } from "./utils.js";

export async function supportedChainsQuery() {
const response = await makeQuery<{chain: string}>(getChain());
const response = await makeQuery<{ chain: string }>(getChain());
return response.data.map((r) => r.chain);
}

export default async function (req: Request) {
export default async function (_req: Request) {
try {
const chains = await supportedChainsQuery();
return new Response(JSON.stringify(chains), { headers: { "Content-Type": "application/json" } });
return toJSON(chains);
} catch (e: any) {
logger.error(e);
prometheus.request_error.inc({pathname: "/chains", status: 400});
prometheus.request_error.inc({ pathname: "/chains", status: 400 });
return new Response(e.message, { status: 400 });
}
}
3 changes: 2 additions & 1 deletion src/fetch/health.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// from: https://github.com/pinax-network/substreams-clock-api/blob/main/src/fetch/health.ts
import client from "../clickhouse/createClient.js";
import { logger } from "../logger.js";
import * as prometheus from "../prometheus.js";

export default async function (req: Request) {
export default async function (_req: Request) {
try {
const response = await client.ping();
if (response.success === false) throw new Error(response.error.message);
Expand Down
Loading

0 comments on commit 2ec04e9

Please sign in to comment.