Skip to content

Commit

Permalink
Improvements/refactoring/updates (#5)
Browse files Browse the repository at this point in the history
* update Github workflows

* add clickhouse methods

* add native Bun fetch

* update queries

* update index

* Update readme

* remove lint workflow

* update readme

* add openapi3-ts

* update comments

* Add toJSON
  • Loading branch information
DenisCarriere authored Oct 25, 2023
1 parent 75fae9f commit 79b2cc3
Show file tree
Hide file tree
Showing 34 changed files with 751 additions and 788 deletions.
4 changes: 0 additions & 4 deletions .env

This file was deleted.

13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# API Server
PORT=8080
HOSTNAME=localhost

# Clickhouse Database
HOST=http://127.0.0.1:8123
DATABASE=default
USERNAME=default
PASSWORD=
MAX_LIMIT=500

# Logging
VERBOSE=true
16 changes: 6 additions & 10 deletions .github/workflows/bun-build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build
name: Generate standalone Bun executable
on:
release:
types: [published]
Expand All @@ -7,21 +7,17 @@ permissions:
contents: write

jobs:
bun-build:
build-and-push-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: "Install Dependencies"
run: bun install

- name: "Build app"
run: bun run build

- run: bun install
- run: bun run test
- run: bun run build
- uses: softprops/action-gh-release@v1
with:
files: |
substreams-erc20-api
substreams-erc20-api
24 changes: 0 additions & 24 deletions .github/workflows/bun-lint.yml

This file was deleted.

55 changes: 9 additions & 46 deletions .github/workflows/bun-test.yml
Original file line number Diff line number Diff line change
@@ -1,51 +1,14 @@
name: Test
name: Bun Test

on:
push:
branches: [develop*, main]
pull_request:
branches: [develop*, main]
on: push

jobs:
bun-test:
build-and-test:
runs-on: ubuntu-latest
environment: dev-test
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install bun
uses: oven-sh/setup-bun@v1

- name: "Install Dependencies"
run: |
bun install
- name: "Setup local Clickhouse DB"
uses: metrico/[email protected]

- name: "Insert mock data into Clickhouse DB for testing"
run: |
curl https://clickhouse.com/ | sh
echo "CREATE DATABASE ${{ secrets.DB_NAME }}" > create.sql
./clickhouse client --queries-file ./create.sql
echo "USE ${{ secrets.DB_NAME }}; CREATE TABLE TotalSupply (address FixedString(40), supply Nullable(String),id String , block_id FixedString(64), module_hash FixedString(40),chain LowCardinality(String) ) ENGINE = MergeTree() ORDER BY (address); CREATE TABLE Contracts (address FixedString(40), name Nullable(String), symbol Nullable(String), decimals Nullable(String), id String , block_id FixedString(64), module_hash FixedString(40),chain LowCardinality(String)) ENGINE = MergeTree() ORDER BY (address); CREATE TABLE balance_changes (contract FixedString(40), owner Nullable(String), old_balance Nullable(String), new_balance Nullable(String), transaction_id Nullable(String), id String , block_id FixedString(64), module_hash FixedString(40),chain LowCardinality(String)) ENGINE = MergeTree() ORDER BY (contract); CREATE TABLE IF NOT EXISTS manifest ( module_hash FixedString(40), module_name String(), chain LowCardinality(String), type String(), ) ENGINE = ReplacingMergeTree PRIMARY KEY (module_hash) ORDER BY (module_hash, module_name); CREATE TABLE IF NOT EXISTS block ( block_id FixedString(64), block_number UInt32(), chain LowCardinality(String), timestamp DateTime64(3, 'UTC'), final_block Bool, ) ENGINE = ReplacingMergeTree PRIMARY KEY (block_id) ORDER BY (block_id, block_number, timestamp);" > setup.sql
./clickhouse client --queries-file ./setup.sql
echo "INSERT INTO ${{ secrets.DB_NAME }}.block (block_id, block_number, chain, timestamp,final_block) VALUES ('0006b5036d09b082543fe4306d98f6ad9b438fa4c139124106018fdfc90ad38f', 15010581, 'eth', '2022-06-23 01:43:01.000',true);" > block.sql
./clickhouse client --queries-file ./block.sql
echo "INSERT INTO ${{ secrets.DB_NAME }}.TotalSupply (address, supply, id, block_id,module_hash,chain) VALUES ('cb9df5dc2ed5d7d3972f601acfe35cdbe57341e0', '100000', '00000000000045166c45af0fc6e4cf31d9e14b9a', '0006b5036d09b082543fe4306d98f6ad9b438fa4c139124106018fdfc90ad38f','9a8fdd0be04bbe23f68ed4c38a02f7cbc96b86fb','eth');" > insert1.sql
./clickhouse client --queries-file ./insert1.sql
echo "INSERT INTO ${{ secrets.DB_NAME }}.Contracts (address, name, symbol, decimals,id, block_id,module_hash,chain) VALUES ('cb9df5dc2ed5d7d3972f601acfe35cdbe57341e0', 'wing', '$wing', '8','00000000000045166c45af0fc6e4cf31d9e14b9a', '0006b5036d09b082543fe4306d98f6ad9b438fa4c139124106018fdfc90ad38f','9a8fdd0be04bbe23f68ed4c38a02f7cbc96b86fb','eth');" > insert2.sql
./clickhouse client --queries-file ./insert2.sql
echo "INSERT INTO ${{ secrets.DB_NAME }}.balance_changes (contract, owner, old_balance, new_balance, transaction_id,id, block_id,module_hash,chain) VALUES ('c083e9947Cf02b8FfC7D3090AE9AEA72DF98FD47', '39fA8c5f2793459D6622857E7D9FbB4BD91766d3', '123123', '3423443', 'ab3612eed62a184eed2ae86bcad766183019cf40f82e5316f4d7c4e61f4baa44','00000000000045166c45af0fc6e4cf31d9e14b9a', '0006b5036d09b082543fe4306d98f6ad9b438fa4c139124106018fdfc90ad38f','9a8fdd0be04bbe23f68ed4c38a02f7cbc96b86fb','eth');" > insert3.sql
./clickhouse client --queries-file ./insert3.sql
- name: "Run test"
run: |
bun test
env:
HOSTNAME: ${{ vars.HOST}}
PORT: ${{ vars.PORT }}
DB_HOST: ${{ vars.DB_HOST }}
DB_NAME: ${{ secrets.DB_NAME }}
DB_USERNAME: ${{ secrets.DB_USERNAME }}
DB_PASSWORD: ""
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- run: bun install
- run: bun run test
6 changes: 3 additions & 3 deletions .github/workflows/ghcr.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: GitHub Container Registry
on:
release:
types: [published]
types: [ published ]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
ghcr:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down Expand Up @@ -39,4 +39,4 @@ jobs:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
labels: ${{ steps.meta.outputs.labels }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
package-lock.json
.env

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
Expand Down
81 changes: 37 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,26 @@
```
bun install
bun run dev
```
# [`Substreams`](https://substreams.streamingfast.io/) ERC20 API

```
open http://localhost:8080
```
[![.github/workflows/bun-test.yml](https://github.com/pinax-network/substreams-erc20-api/actions/workflows/bun-test.yml/badge.svg)](https://github.com/pinax-network/substreams-erc20-api/actions/workflows/bun-test.yml)

███████╗██████╗░░█████╗░██████╗░░█████╗░  ░█████╗░██████╗░██╗
██╔════╝██╔══██╗██╔══██╗╚════██╗██╔══██╗  ██╔══██╗██╔══██╗██║
█████╗░░██████╔╝██║░░╚═╝░░███╔═╝██║░░██║  ███████║██████╔╝██║
██╔══╝░░██╔══██╗██║░░██╗██╔══╝░░██║░░██║  ██╔══██║██╔═══╝░██║
███████╗██║░░██║╚█████╔╝███████╗╚█████╔╝  ██║░░██║██║░░░░░██║
╚══════╝╚═╝░░╚═╝░╚════╝░╚══════╝░╚════╝░  ╚═╝░░╚═╝╚═╝░░░░░╚═╝

# [`Substreams`](https://substreams.streamingfast.io/) ERC20 API
> ERC-20 Balance, Supply, Contract API
## REST API

| Pathname | Description |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| GET `/` | Banner |
| GET `/supply?address=<contract address>&block=<Optional Block Number>` | Returns the total supply of a contract |
| GET `/contract?address=<contract address>` | Returns Contract information (name,symbol,decimals) |
| GET `/balance?wallet?<wallet address>&address=<contract address>&block=<Optional Block Number>` | Returns the wallet balance |
| GET `/openapi` | [OpenAPI v3 JSON](https://spec.openapis.org/oas/v3.0.0) |
| GET `/swagger` | [Swagger UI](https://swagger.io/resources/open-api/) |
| Pathname | Description |
| ----------------------|-------------------------------------------------------- |
| GET `/` | [Swagger UI](https://swagger.io/resources/open-api/)
| GET `/chains` | Available `chains`
| GET `/supply` | ERC20 total supply
| GET `/contract` | ERC20 contract information (name,symbol,decimals)
| GET `/balance` | ERC20 balance changes
| GET `/health` | Health check
| GET `/metrics` | Prometheus metrics
| GET `/openapi` | [OpenAPI v3 JSON](https://spec.openapis.org/oas/v3.0.0)

## Requirements

- [Clickhouse](clickhouse.com/)

Additionnaly to pull data directly from a substream:

- [Substreams Sink Clickhouse](https://github.com/pinax-network/substreams-sink-clickhouse/)
- [ClickHouse](clickhouse.com/)
- [Substreams Sink ClickHouse](https://github.com/pinax-network/substreams-sink-clickhouse/)

## Quickstart

Expand All @@ -54,13 +41,19 @@ $ chmod +x ./substreams-erc20-api
## `.env` Environment variables

```env
# Optional
# API Server
PORT=8080
HOSTNAME=localhost
DB_HOST=http://localhost:8123
DB_NAME=demo
DB_USERNAME=default
DB_PASSWORD=
# Clickhouse Database
HOST=http://127.0.0.1:8123
DATABASE=default
USERNAME=default
PASSWORD=
MAX_LIMIT=500
# Logging
VERBOSE=true
```

## Help
Expand All @@ -69,19 +62,19 @@ DB_PASSWORD=
$ ./substreams-erc20-api --help
Usage: substreams-erc20-api [options]

Timestamps <> Block numbers conversion for your favorite chains
ERC20 API powered by Substreams

Options:
--port <int> Server listen on HTTP port (default: "8080", env: PORT)
--hostname <string> Server listen on HTTP hostname (default: "localhost", env: HOST)
--db-host <string> Clickhouse DB HTTP hostname (default: "http://localhost:8123", env: dbHost)
--name <string> Clickhouse DB table name (default: "demo", env: DB_NAME)
--username <string> Clickhouse DB username (default: "default", env: DB_USERNAME)
--password <string> Clickhouse DB password (default: "", env: DB_PASSWORD)
--max-elements-queried <string> Maximum number of query elements when using arrays as parameters (default: 10, env: MAX_ELEMENTS_QUERIED)
--verbose <boolean> Enable verbose logging (default: false, env: VERBOSE)
-V, --version output the version number
-h, --help display help for command
-V, --version output the version number
-p, --port <number> HTTP port on which to attach the API (default: "8080", env: PORT)
-v, --verbose <boolean> Enable verbose logging (choices: "true", "false", default: false, env: VERBOSE)
--hostname <string> Server listen on HTTP hostname (default: "localhost", env: HOSTNAME)
--host <string> Database HTTP hostname (default: "http://localhost:8123", env: HOST)
--username <string> Database user (default: "default", env: USERNAME)
--password <string> Password associated with the specified username (default: "", env: PASSWORD)
--database <string> The database to use inside ClickHouse (default: "default", env: DATABASE)
--max-limit <number> Maximum LIMIT queries (default: 10000, env: MAX_LIMIT)
-h, --help display help for command
```

## Docker environment
Expand Down
Binary file modified bun.lockb
Binary file not shown.
19 changes: 19 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// from: https://github.com/pinax-network/substreams-clock-api/blob/main/index.ts
import { config } from "./src/config";
import { logger } from "./src/logger";
import GET from "./src/fetch/GET";
import * as prometheus from "./src/prometheus.js";

if (config.verbose) logger.enable();

const app = Bun.serve({
hostname: config.hostname,
port: config.port,
fetch(req: Request) {
if (req.method === "GET") return GET(req);
prometheus.request_error.inc({pathname: new URL(req.url).pathname, status: 400});
return new Response("Invalid request", { status: 400 });
}
});

logger.info(`Server listening on http://${app.hostname}:${app.port}`);
35 changes: 18 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"description": "ERC20 API powered by Substreams",
"version": "v0.0.1",
"name": "substreams-erc20-api",
"repository": "https://github.com/pinax-network/substreams-erc20-api",
"type": "module",
"license": "MIT",
"homepage": "https://github.com/pinax-network/substreams-erc20-api",
"authors": [
{
"name": "Mathieu Lefebvre",
Expand All @@ -16,25 +18,24 @@
}
],
"scripts": {
"dev": "bun run --watch src/index.ts",
"lint": "bun run tsc --noEmit --skipLibCheck --pretty",
"test": "bun test test/*.spec.ts --coverage",
"build": "bun build --compile ./src/index.ts --outfile substreams-erc20-api"
"start": "bun index.ts",
"dev": "bun --watch index.ts",
"pretest": "bunx tsc --noEmit",
"test": "bun test",
"build": "bun build --compile ./index.ts --outfile substreams-erc20-api"
},
"license": "MIT",
"dependencies": {
"@clickhouse/client-web": "^0.2.2",
"@hono/zod-openapi": "^0.7.2",
"@sinclair/typebox": "^0.31.17",
"commander": "^11.1.0",
"dotenv": "^16.3.1",
"ethers": "^6.8.0",
"hono": "^3.7.2",
"tslog": "^4.9.2",
"zod": "^3.22.4"
"@clickhouse/client-web": "latest",
"commander": "latest",
"dotenv": "latest",
"ethers": "latest",
"openapi3-ts": "latest",
"prom-client": "latest",
"tslog": "latest",
"zod": "latest"
},
"devDependencies": {
"bun-types": "^0.6.2",
"typescript": "^5.2.2"
"bun-types": "latest",
"typescript": "latest"
}
}
20 changes: 20 additions & 0 deletions src/clickhouse/createClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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";
import { APP_NAME, config } from "../config";

const client = createClient({
...config,
clickhouse_settings: {
allow_experimental_object_type: 1,
},
application: APP_NAME,
})

// These overrides should not be required but the @clickhouse/client-web instance
// does not work well with Bun's implementation of Node streams.
// https://github.com/oven-sh/bun/issues/5470
client.command = client.exec;
client.ping = ping;

export default client;
Loading

0 comments on commit 79b2cc3

Please sign in to comment.