Skip to content

Commit c71a294

Browse files
committed
Refactoring the provider and improving automatic service queries
1 parent fd20eaf commit c71a294

11 files changed

+286
-217
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ npm install @blockcore/provider
3535
## Usage
3636

3737
```ts
38-
import { Provider } from '@blockcore/provider';
38+
import { Provider } from "@blockcore/provider";
3939

4040
let provider = new Provider();
4141
const result: any = await provider.getCirculatingSupply();
@@ -44,3 +44,15 @@ const result: any = await provider.getCirculatingSupply();
4444
Screen recording demonstrating the use of the Blockcore Web Provider:
4545

4646
![](doc/blockcore-provider.gif)
47+
48+
## Development
49+
50+
### Build
51+
52+
Simply run `npm run build` to build the library. Also run `npm run lint` to verify the code syntax.
53+
54+
### Testing
55+
56+
The library is using `ava` for tests. Please refer to the documentation on how to write tests:
57+
58+
[https://github.com/avajs/ava/blob/main/docs/01-writing-tests.md](https://github.com/avajs/ava/blob/main/docs/01-writing-tests.md)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
},
6060
"dependencies": {
6161
"@blockcore/coininfo": "^5.2.2",
62-
"@blockcore/dns": "^0.0.4",
62+
"@blockcore/dns": "^0.0.5",
6363
"node-fetch": "^3.2.9"
6464
}
6565
}

src/BlockcoreProvider.ts

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@ import fetch, { Response } from 'node-fetch';
22
import { RequestArguments, Address, ChainListEntry, RichListEntry, Supply, WalletListEntry } from './types.js';
33
import coininfo from '@blockcore/coininfo';
44

5+
/** Provider that interacts with the wallet. */
56
export class BlockcoreProvider {
6-
private baseUrl: string;
7+
// private baseUrl: string;
78

8-
public constructor(baseUrlOrNetwork?: string) {
9-
baseUrlOrNetwork = baseUrlOrNetwork || 'CITY';
9+
public constructor() {
10+
// baseUrlOrNetwork = baseUrlOrNetwork || 'CITY';
1011

11-
if (baseUrlOrNetwork.indexOf('http') > -1) {
12-
this.baseUrl = baseUrlOrNetwork;
13-
} else {
14-
this.baseUrl = this.getNetworkUrl(baseUrlOrNetwork);
15-
}
12+
// if (baseUrlOrNetwork.indexOf('http') > -1) {
13+
// this.baseUrl = baseUrlOrNetwork;
14+
// } else {
15+
// this.baseUrl = this.getNetworkUrl(baseUrlOrNetwork);
16+
// }
1617
}
1718

18-
setProvider(provider: string) {
19-
this.baseUrl = provider;
20-
}
19+
// setProvider(provider: string) {
20+
// this.baseUrl = provider;
21+
// }
2122

2223
on(event: string, callback: any) {
2324
console.log(event, callback);
@@ -50,41 +51,17 @@ export class BlockcoreProvider {
5051
return null;
5152
}
5253

53-
private async fetchText(url: string): Promise<string> {
54-
const response = await this.fetchUrl(url);
55-
return response.text();
56-
}
57-
58-
private async fetchJson<T>(url: string): Promise<T> {
59-
const response = await this.fetchUrl(url);
60-
return response.json() as Promise<T>;
61-
}
62-
63-
private async fetchUrl(url: string): Promise<Response> {
64-
return await fetch(url, {
65-
method: 'GET',
66-
// mode: 'cors',
67-
// cache: 'no-cache',
68-
// credentials: 'same-origin',
69-
headers: {
70-
'Content-Type': 'application/json',
71-
},
72-
redirect: 'follow',
73-
referrerPolicy: 'no-referrer',
74-
});
75-
}
54+
// public setNetwork(network: string): void {
55+
// this.baseUrl = this.getNetworkUrl(network);
56+
// }
7657

77-
public setNetwork(network: string): void {
78-
this.baseUrl = this.getNetworkUrl(network);
79-
}
58+
// public getNetworkUrl(network: string): string {
59+
// return `https://${network.toLowerCase()}.indexer.blockcore.net`;
60+
// }
8061

81-
public getNetworkUrl(network: string): string {
82-
return `https://${network.toLowerCase()}.indexer.blockcore.net`;
83-
}
84-
85-
public getBaseUrl(): string {
86-
return this.baseUrl;
87-
}
62+
// public getBaseUrl(): string {
63+
// return this.baseUrl;
64+
// }
8865

8966
/** Returns network definition from local package, no external requests. */
9067
public getNetwork(network: string) {

src/IndexerProvider.ts

Lines changed: 79 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,165 @@
11
import fetch, { Response } from 'node-fetch';
22
import { RequestArguments, Address, ChainListEntry, EIP1193Provider, RichListEntry, Supply, WalletListEntry } from './types.js';
3+
import { BlockcoreDns, DnsListEntry, ServiceListEntry } from '@blockcore/dns';
4+
import { WebRequest } from './Request.js';
35

46
export class IndexerProvider {
5-
private baseUrl: string;
7+
private dns: BlockcoreDns;
8+
private nameservers: DnsListEntry[] = [];
9+
private services: ServiceListEntry[] = [];
10+
private currentServices: ServiceListEntry[] = [];
11+
private network = 'STRAX'; // Should we default to BTC?
612

7-
public constructor(baseUrlOrNetwork?: string) {
8-
baseUrlOrNetwork = baseUrlOrNetwork || 'CITY';
9-
10-
if (baseUrlOrNetwork.indexOf('http') > -1) {
11-
this.baseUrl = baseUrlOrNetwork;
12-
} else {
13-
this.baseUrl = this.getNetworkUrl(baseUrlOrNetwork);
14-
}
13+
public constructor() {
14+
this.dns = new BlockcoreDns('');
1515
}
1616

17-
setProvider(provider: string) {
18-
this.baseUrl = provider;
17+
setNetwork(network: string) {
18+
this.network = network;
19+
this.filterServices();
1920
}
2021

21-
on(event: string, callback: any) {
22-
console.log(event, callback);
23-
// "accountsChanged"
24-
// "chainChanged"
25-
// "networkChanged"
22+
filterServices() {
23+
this.currentServices = this.services.filter((s) => s.symbol === this.network && s.online === true);
2624
}
2725

28-
private async fetchText(url: string): Promise<string> {
29-
const response = await this.fetchUrl(url);
30-
return response.text();
26+
/** Attempts to load the latest status of all services from all known nameservers. */
27+
async load() {
28+
this.nameservers = await BlockcoreDns.getDnsServers();
29+
30+
const servicesMap = new Map();
31+
32+
for (let i = 0; i < this.nameservers.length; i++) {
33+
const nameserver = this.nameservers[i];
34+
35+
if (!nameserver) {
36+
continue;
37+
}
38+
39+
this.dns.setActiveServer(nameserver.url);
40+
41+
const services = await this.dns.getServicesByType('Indexer');
42+
43+
services.forEach((item) => servicesMap.set(item.domain, { ...servicesMap.get(item.domain), ...item }));
44+
}
45+
46+
this.services = Array.from(servicesMap.values());
47+
48+
this.filterServices();
3149
}
3250

33-
private async fetchJson<T>(url: string): Promise<T> {
34-
const response = await this.fetchUrl(url);
35-
return response.json() as Promise<T>;
51+
getNameServers() {
52+
return this.nameservers;
3653
}
3754

38-
private async fetchUrl(url: string): Promise<Response> {
39-
return await fetch(url, {
40-
method: 'GET',
41-
// mode: 'cors',
42-
// cache: 'no-cache',
43-
// credentials: 'same-origin',
44-
headers: {
45-
'Content-Type': 'application/json',
46-
},
47-
redirect: 'follow',
48-
referrerPolicy: 'no-referrer',
49-
});
55+
async getIndexersByNetwork(network: string) {
56+
return this.dns.getServicesByTypeAndNetwork('Indexer', network);
5057
}
5158

52-
public setNetwork(network: string): void {
53-
this.baseUrl = this.getNetworkUrl(network);
59+
on(event: string, callback: any) {
60+
console.log(event, callback);
61+
// "accountsChanged"
62+
// "chainChanged"
63+
// "networkChanged"
5464
}
5565

56-
public getNetworkUrl(network: string): string {
57-
return `https://${network.toLowerCase()}.indexer.blockcore.net`;
66+
private getRandomInt(max: number) {
67+
return Math.floor(Math.random() * max);
5868
}
5969

60-
public getBaseUrl(): string {
61-
return this.baseUrl;
70+
private getUrl(): string | undefined {
71+
// TODO: This can be simplified, I'm just too tired to refactor right now.
72+
if (this.currentServices.length > 1) {
73+
const serviceIndex = this.getRandomInt(this.currentServices.length);
74+
return `https://${this.currentServices[serviceIndex]?.domain}`;
75+
} else if (this.currentServices.length == 1) {
76+
return `https://${this.currentServices[0]?.domain}`;
77+
} else {
78+
return undefined;
79+
}
6280
}
6381

6482
//** Returns the result from the officially hosted list of Blockcore supported chains. */
6583
public getNetworks() {
66-
return this.fetchJson<ChainListEntry[]>('https://chains.blockcore.net/CHAINS.json');
84+
return WebRequest.fetchJson<ChainListEntry[]>('https://chains.blockcore.net/CHAINS.json');
6785
}
6886

6987
public getSupply() {
70-
return this.fetchJson<Supply>(this.baseUrl + '/api/insight/supply');
88+
return WebRequest.fetchJson<Supply>(this.getUrl() + '/api/insight/supply');
7189
}
7290

7391
public async getCirculatingSupply() {
74-
return this.fetchText(this.baseUrl + '/api/insight/supply/circulating');
92+
return WebRequest.fetchText(this.getUrl() + '/api/insight/supply/circulating');
7593
}
7694

7795
public async getTotalSupply() {
78-
return this.fetchText(this.baseUrl + '/api/insight/supply/total');
96+
return WebRequest.fetchText(this.getUrl() + '/api/insight/supply/total');
7997
}
8098

8199
public async getEstimateRewards() {
82-
return this.fetchText(this.baseUrl + '/api/insight/rewards');
100+
return WebRequest.fetchText(this.getUrl() + '/api/insight/rewards');
83101
}
84102
public async getWallets() {
85-
return this.fetchJson<WalletListEntry[]>(this.baseUrl + '/api/insight/wallets');
103+
return WebRequest.fetchJson<WalletListEntry[]>(this.getUrl() + '/api/insight/wallets');
86104
}
87105

88106
public async getRichList() {
89-
return this.fetchJson<RichListEntry[]>(this.baseUrl + '/api/insight/richlist');
107+
return WebRequest.fetchJson<RichListEntry[]>(this.getUrl() + '/api/insight/richlist');
90108
}
91109

92110
public async getAddress(address: string) {
93-
return this.fetchJson<Address>(`${this.baseUrl}/api/query/address/${address}`);
111+
return WebRequest.fetchJson<Address>(`${this.getUrl()}/api/query/address/${address}`);
94112
}
95113

96114
public async getAddressTransactions(address: string) {
97-
return this.fetchJson(`${this.baseUrl}/api/query/address/${address}/transactions`);
115+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/address/${address}/transactions`);
98116
}
99117

100118
public async getAddressUnconfirmedTransactions(address: string) {
101-
return this.fetchJson(`${this.baseUrl}/api/query/address/${address}/transactions/unconfirmed`);
119+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/address/${address}/transactions/unconfirmed`);
102120
}
103121

104122
public async getAddressSpentTransactions(address: string) {
105-
return this.fetchJson(`${this.baseUrl}/api/query/address/${address}/transactions/spent`);
123+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/address/${address}/transactions/spent`);
106124
}
107125

108126
public async getAddressUnspentTransactions(address: string) {
109-
return this.fetchJson(`${this.baseUrl}/api/query/address/${address}/transactions/unspent`);
127+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/address/${address}/transactions/unspent`);
110128
}
111129

112130
public async getMempoolTransactions() {
113-
return this.fetchJson(`${this.baseUrl}/api/query/mempool/transactions`);
131+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/mempool/transactions`);
114132
}
115133

116134
public async getMempoolTransactionsCount() {
117-
return this.fetchText(`${this.baseUrl}/api/query/mempool/transactions/count`);
135+
return WebRequest.fetchText(`${this.getUrl()}/api/query/mempool/transactions/count`);
118136
}
119137

120138
public async getTransactionById(id: string) {
121-
return this.fetchJson(`${this.baseUrl}/api/query/transaction/${id}`);
139+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/transaction/${id}`);
122140
}
123141

124142
public async getBlock() {
125-
return this.fetchJson(`${this.baseUrl}/api/query/block`);
143+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/block`);
126144
}
127145

128146
public async getBlockTransactionsByHash(hash: string) {
129-
return this.fetchJson(`${this.baseUrl}/api/query/block/${hash}/transactions`);
147+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/block/${hash}/transactions`);
130148
}
131149

132150
public async getBlockByHash(hash: string) {
133-
return this.fetchJson(`${this.baseUrl}/api/query/block/${hash}`);
151+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/block/${hash}`);
134152
}
135153

136154
public async getBlockByIndex(index: string) {
137-
return this.fetchJson(`${this.baseUrl}/api/query/block/index/${index}`);
155+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/block/index/${index}`);
138156
}
139157

140158
public async getBlockTransactionsByIndex(index: string) {
141-
return this.fetchJson(`${this.baseUrl}/api/query/block/index/${index}/transactions`);
159+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/block/index/${index}/transactions`);
142160
}
143161

144162
public async getLatestBlock() {
145-
return this.fetchJson(`${this.baseUrl}/api/query/block/latest`);
163+
return WebRequest.fetchJson(`${this.getUrl()}/api/query/block/latest`);
146164
}
147165
}

src/Request.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import fetch, { Response } from 'node-fetch';
2+
3+
export class WebRequest {
4+
public static async fetchText(url: string): Promise<string> {
5+
const response = await WebRequest.fetchUrl(url);
6+
return response.text();
7+
}
8+
9+
public static async fetchJson<T>(url: string): Promise<T> {
10+
const response = await WebRequest.fetchUrl(url);
11+
return response.json() as Promise<T>;
12+
}
13+
14+
public static async fetchUrl(url: string): Promise<Response> {
15+
return await fetch(url, {
16+
method: 'GET',
17+
// mode: 'cors',
18+
// cache: 'no-cache',
19+
// credentials: 'same-origin',
20+
headers: {
21+
'Content-Type': 'application/json',
22+
},
23+
redirect: 'follow',
24+
referrerPolicy: 'no-referrer',
25+
});
26+
}
27+
}

0 commit comments

Comments
 (0)