Skip to content

Commit e262beb

Browse files
committed
bitquery
1 parent 06c0db0 commit e262beb

3 files changed

Lines changed: 333 additions & 17 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* `SEARCH_API_ADDR` - hivesearcher api endpoint
2727
* `SEARCH_API_SECRET` - hivesearcher api auth token
2828
* `CHAINSTACK_API_KEY` - Chainstack API key used to enumerate nodes and request balances directly from each network-specific endpoint.
29+
* `BITQUERY_API_KEY` - Bitquery API key (a.k.a. access token) that enables balance lookups when the Bitquery provider is requested. You can also set it via `BITQUERY_ACCESS_TOKEN`.
2930

3031
##### Start api in dev
3132
`$ yarn start`
@@ -52,6 +53,7 @@ Configure the instance using following environment variables:
5253
* `SEARCH_API_ADDR`
5354
* `SEARCH_API_SECRET`
5455
* `CHAINSTACK_API_KEY`
56+
* `BITQUERY_API_KEY` (or `BITQUERY_ACCESS_TOKEN`)
5557

5658
```bash
5759
docker run -it --rm -p 3000:3000 -e PRIVATE_API_ADDR=https://api.example.com -e PRIVATE_API_AUTH=verysecretpassword ecency/api:latest

src/server/handlers/private-api.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import axios, { AxiosRequestConfig } from "axios";
44
import { cryptoUtils, Signature, Client } from '@hiveio/dhive'
55

66
import { announcements } from "./announcements";
7-
import { apiRequest, getPromotedEntries } from "../helper";
7+
import {
8+
apiRequest,
9+
getPromotedEntries,
10+
ChainBalanceResponse,
11+
parseBalanceProvider,
12+
fetchBitqueryBalance,
13+
} from "../helper";
814

915
import { pipe } from "../util";
1016
import { cache } from '../cache';
@@ -201,18 +207,10 @@ interface AxiosNodeConfig {
201207
auth?: AxiosAuthConfig;
202208
}
203209

204-
interface NormalizedBalanceResponse {
205-
chain: string;
206-
balance: string | null;
207-
unit: string;
208-
raw: unknown;
209-
nodeId: string;
210-
}
211-
212210
interface ChainHandler {
213211
validateAddress?: (address: string) => boolean;
214212
selectNode: (nodes: ChainstackNode[]) => ChainstackNode | null;
215-
fetchBalance: (node: ChainstackNode, address: string) => Promise<NormalizedBalanceResponse>;
213+
fetchBalance: (node: ChainstackNode, address: string) => Promise<ChainBalanceResponse>;
216214
}
217215

218216
let cachedNodes: ChainstackNode[] | null = null;
@@ -410,7 +408,7 @@ const parseHexBalance = (value: unknown): string => {
410408
}
411409
};
412410

413-
const fetchEvmBalance = async (chain: string, node: ChainstackNode, address: string): Promise<NormalizedBalanceResponse> => {
411+
const fetchEvmBalance = async (chain: string, node: ChainstackNode, address: string): Promise<ChainBalanceResponse> => {
414412
const endpoint = ensureHttpsEndpoint(node);
415413
const config = buildNodeAxiosConfig(node);
416414

@@ -436,10 +434,11 @@ const fetchEvmBalance = async (chain: string, node: ChainstackNode, address: str
436434
unit: "wei",
437435
raw: data,
438436
nodeId: node.id,
437+
provider: "chainstack",
439438
};
440439
};
441440

442-
const fetchSolanaBalance = async (node: ChainstackNode, address: string): Promise<NormalizedBalanceResponse> => {
441+
const fetchSolanaBalance = async (node: ChainstackNode, address: string): Promise<ChainBalanceResponse> => {
443442
const endpoint = ensureHttpsEndpoint(node);
444443
const config = buildNodeAxiosConfig(node);
445444

@@ -469,10 +468,11 @@ const fetchSolanaBalance = async (node: ChainstackNode, address: string): Promis
469468
unit: "lamports",
470469
raw: data,
471470
nodeId: node.id,
471+
provider: "chainstack",
472472
};
473473
};
474474

475-
const fetchTronBalance = async (node: ChainstackNode, address: string): Promise<NormalizedBalanceResponse> => {
475+
const fetchTronBalance = async (node: ChainstackNode, address: string): Promise<ChainBalanceResponse> => {
476476
const endpoint = ensureHttpsEndpoint(node);
477477
const config = buildNodeAxiosConfig(node);
478478

@@ -509,6 +509,7 @@ const fetchTronBalance = async (node: ChainstackNode, address: string): Promise<
509509
unit: "sun",
510510
raw: data,
511511
nodeId: node.id,
512+
provider: "chainstack",
512513
};
513514
};
514515

@@ -705,7 +706,7 @@ const ensureTonAuthKeyInEndpoint = (endpoint: string, authKey?: string): string
705706
}
706707
};
707708

708-
const fetchTonBalance = async (node: ChainstackNode, address: string): Promise<NormalizedBalanceResponse> => {
709+
const fetchTonBalance = async (node: ChainstackNode, address: string): Promise<ChainBalanceResponse> => {
709710
const endpointCandidates = [node.details?.toncenter_api_v3, node.details?.toncenter_api_v2]
710711
.filter((endpoint): endpoint is string => Boolean(endpoint))
711712
.map((endpoint) => ensureTonAuthKeyInEndpoint(endpoint, node.details?.auth_key))
@@ -800,6 +801,7 @@ const fetchTonBalance = async (node: ChainstackNode, address: string): Promise<N
800801
unit: "nanotons",
801802
raw: data,
802803
nodeId: node.id,
804+
provider: "chainstack",
803805
};
804806
};
805807

@@ -823,7 +825,7 @@ const normalizeAptosAddress = (address: string): string => {
823825
return `0x${padded.toLowerCase()}`;
824826
};
825827

826-
const fetchAptosBalance = async (node: ChainstackNode, address: string): Promise<NormalizedBalanceResponse> => {
828+
const fetchAptosBalance = async (node: ChainstackNode, address: string): Promise<ChainBalanceResponse> => {
827829
const endpoint = ensureHttpsEndpoint(node);
828830
const config = buildNodeAxiosConfig(node);
829831
const normalizedAddress = normalizeAptosAddress(address);
@@ -846,6 +848,7 @@ const fetchAptosBalance = async (node: ChainstackNode, address: string): Promise
846848
unit: "octas",
847849
raw: data,
848850
nodeId: node.id,
851+
provider: "chainstack",
849852
};
850853
} catch (err) {
851854
if (axios.isAxiosError(err) && err.response?.status === 404) {
@@ -874,6 +877,7 @@ const fetchAptosBalance = async (node: ChainstackNode, address: string): Promise
874877
unit: "octas",
875878
raw: viewData,
876879
nodeId: node.id,
880+
provider: "chainstack",
877881
};
878882
}
879883
} catch (viewError) {
@@ -888,6 +892,7 @@ const fetchAptosBalance = async (node: ChainstackNode, address: string): Promise
888892
unit: "octas",
889893
raw: err.response?.data,
890894
nodeId: node.id,
895+
provider: "chainstack",
891896
};
892897
}
893898

@@ -1036,7 +1041,7 @@ const formatBitcoinFromSats = (value: string): string => {
10361041

10371042
const BITCOIN_RPC_TIMEOUT_MS = 180_000;
10381043

1039-
const fetchBitcoinBalance = async (node: ChainstackNode, address: string): Promise<NormalizedBalanceResponse> => {
1044+
const fetchBitcoinBalance = async (node: ChainstackNode, address: string): Promise<ChainBalanceResponse> => {
10401045
const endpoint = ensureHttpsEndpoint(node);
10411046
const config: AxiosRequestConfig = {
10421047
...buildNodeAxiosConfig(node),
@@ -1168,6 +1173,7 @@ const fetchBitcoinBalance = async (node: ChainstackNode, address: string): Promi
11681173
unit: "btc",
11691174
raw: scannedBalance.raw,
11701175
nodeId: node.id,
1176+
provider: "chainstack",
11711177
};
11721178
}
11731179

@@ -1180,6 +1186,7 @@ const fetchBitcoinBalance = async (node: ChainstackNode, address: string): Promi
11801186
unit: "btc",
11811187
raw: directBalance.raw,
11821188
nodeId: node.id,
1189+
provider: "chainstack",
11831190
};
11841191
}
11851192

@@ -1249,6 +1256,8 @@ export const balance = async (req: express.Request, res: express.Response) => {
12491256
return;
12501257
}
12511258

1259+
const provider = parseBalanceProvider(req.query.provider);
1260+
12521261
if (normalizedChain === "btc") {
12531262
const extendedTimeout = BITCOIN_RPC_TIMEOUT_MS + 30_000;
12541263

@@ -1267,6 +1276,13 @@ export const balance = async (req: express.Request, res: express.Response) => {
12671276
return;
12681277
}
12691278

1279+
if (provider === "bitquery") {
1280+
const balanceResponse = await fetchBitqueryBalance(normalizedChain, address);
1281+
1282+
res.status(200).json(balanceResponse);
1283+
return;
1284+
}
1285+
12701286
const nodes = await fetchChainstackNodes();
12711287
const node = handler.selectNode(nodes);
12721288

@@ -1292,7 +1308,12 @@ export const balance = async (req: express.Request, res: express.Response) => {
12921308
return;
12931309
}
12941310

1295-
res.status(502).send("Failed to fetch balance from Chainstack");
1311+
const failureMessage =
1312+
provider === "bitquery"
1313+
? "Failed to fetch balance from Bitquery"
1314+
: "Failed to fetch balance from Chainstack";
1315+
1316+
res.status(502).send(failureMessage);
12961317
}
12971318
};
12981319

0 commit comments

Comments
 (0)