diff --git a/src/config/utils/nodes.ts b/src/config/utils/nodes.ts index 70a7318d8..e99a87005 100644 --- a/src/config/utils/nodes.ts +++ b/src/config/utils/nodes.ts @@ -1,4 +1,4 @@ -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { BlockchainSymbol } from './types' import config from '../index' diff --git a/src/lib/nodes/abstract.node.ts b/src/lib/nodes/abstract.node.ts index 9d2161efe..b75cc22f3 100644 --- a/src/lib/nodes/abstract.node.ts +++ b/src/lib/nodes/abstract.node.ts @@ -1,3 +1,4 @@ +import type { NodeInfo } from '@/types/wallets/index.ts' import { getHealthCheckInterval } from './utils/getHealthcheckConfig' import { TNodeLabel } from './constants' import { HealthcheckInterval, HealthcheckResult, NodeKind, NodeStatus, NodeType } from './types' @@ -29,6 +30,10 @@ export abstract class Node { */ wsPort = '36668' + /** + * Node alternative IP + */ + altIp?: string /** * Node base URL */ @@ -56,10 +61,22 @@ export abstract class Node { hasSupportedProtocol = true // Healthcheck related params + /** + * Indicates whether a node with alternative IP is available + */ + altIpAvailable = false + /** + * Indicates whether a node with main URL is available + */ + mainUrlAvailable = true /** * Indicates whether node is available. */ online = true + /** + * Indicates whether prefer a node with alternative IP or not + */ + preferAltIp = false /** * Node ping estimation */ @@ -97,13 +114,16 @@ export abstract class Node { healthcheckInProgress = false constructor( - url: string, + endpoint: NodeInfo, type: NodeType, kind: NodeKind, label: TNodeLabel, version = '', minNodeVersion = '' ) { + const { alt_ip, url } = endpoint + + this.altIp = alt_ip this.url = url this.type = type this.label = label @@ -134,8 +154,21 @@ export abstract class Node { this.height = health.height this.ping = health.ping this.online = true + + if (this.preferAltIp) { + this.altIpAvailable = true + } else { + this.mainUrlAvailable = true + } } catch { - this.online = false + if (this.preferAltIp) { + this.altIpAvailable = false + this.preferAltIp = false + this.online = false + } else if (this.mainUrlAvailable) { + this.mainUrlAvailable = false + this.preferAltIp = true + } } finally { this.healthcheckInProgress = false } @@ -164,6 +197,7 @@ export abstract class Node { getStatus() { return { + alt_ip: this.altIp, url: this.url, port: this.port, hostname: this.hostname, diff --git a/src/lib/nodes/adm/AdmClient.ts b/src/lib/nodes/adm/AdmClient.ts index 1d0d42485..990636f97 100644 --- a/src/lib/nodes/adm/AdmClient.ts +++ b/src/lib/nodes/adm/AdmClient.ts @@ -5,6 +5,7 @@ import { RegisterChatMessageTransaction } from '@/lib/schema/client' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { AdmNode, Payload, RequestConfig } from './AdmNode' import { Client } from '../abstract.client' @@ -16,7 +17,7 @@ import { Client } from '../abstract.client' * is not available at the moment. */ export class AdmClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('adm', 'node', NODE_LABELS.AdmNode) this.nodes = endpoints.map((endpoint) => new AdmNode(endpoint, minNodeVersion)) this.minNodeVersion = minNodeVersion @@ -75,7 +76,9 @@ export class AdmClient extends Client { return this.getNode().timeDelta } - async sendChatTransaction(transaction: RegisterChatMessageTransaction): Promise { + async sendChatTransaction( + transaction: RegisterChatMessageTransaction + ): Promise { return this.post('/api/chats/process', () => ({ transaction })) } } diff --git a/src/lib/nodes/adm/AdmNode.ts b/src/lib/nodes/adm/AdmNode.ts index fe0563169..370d2b940 100644 --- a/src/lib/nodes/adm/AdmNode.ts +++ b/src/lib/nodes/adm/AdmNode.ts @@ -4,6 +4,7 @@ import { GetNodeStatusResponseDto } from '@/lib/schema/client' import axios, { AxiosInstance, AxiosRequestConfig } from 'axios' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' type FetchNodeInfoResult = { socketSupport: boolean @@ -18,6 +19,7 @@ export type Payload = (ctx: AdmNode): Record } export type RequestConfig

= { + baseURL?: string url: string method?: string payload?: P @@ -28,8 +30,8 @@ export type RequestConfig

= { * to the node and verify is status (online/offline, version, ping, etc.) */ export class AdmNode extends Node { - constructor(url: string, minNodeVersion = '0.0.0') { - super(url, 'adm', 'node', NODE_LABELS.AdmNode, '', minNodeVersion) + constructor(endpoint: NodeInfo, minNodeVersion = '0.0.0') { + super(endpoint, 'adm', 'node', NODE_LABELS.AdmNode, '', minNodeVersion) this.wsPort = '36668' // default wsPort this.wsProtocol = this.protocol === 'https:' ? 'wss:' : 'ws:' @@ -52,6 +54,7 @@ export class AdmNode extends Node { const { url, method = 'get', payload } = cfg const config: AxiosRequestConfig = { + baseURL: this.preferAltIp ? this.altIp : this.url, url, method: method.toLowerCase(), [method === 'get' ? 'params' : 'data']: @@ -72,7 +75,6 @@ export class AdmNode extends Node { // According to https://github.com/axios/axios#handling-errors this means, that request was sent, // but server could not respond. if (!error.response && error.request) { - this.online = false throw new NodeOfflineError() } throw error diff --git a/src/lib/nodes/adm/index.ts b/src/lib/nodes/adm/index.ts index 134177228..bc2fba629 100644 --- a/src/lib/nodes/adm/index.ts +++ b/src/lib/nodes/adm/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { AdmClient } from './AdmClient' -const endpoints = (config.adm.nodes.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.adm.nodes.list as NodeInfo[] + export const adm = new AdmClient(endpoints, config.adm.nodes.minVersion) export default adm diff --git a/src/lib/nodes/btc-indexer/BtcIndexer.ts b/src/lib/nodes/btc-indexer/BtcIndexer.ts index 6bc505ae6..f1088a9bb 100644 --- a/src/lib/nodes/btc-indexer/BtcIndexer.ts +++ b/src/lib/nodes/btc-indexer/BtcIndexer.ts @@ -2,14 +2,15 @@ import { createBtcLikeClient } from '../utils/createBtcLikeClient' import { AxiosInstance, AxiosRequestConfig } from 'axios' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' /** * Encapsulates a node. Provides methods to send API-requests * to the node and verify is status (online/offline, version, ping, etc.) */ export class BtcIndexer extends Node { - constructor(url: string) { - super(url, 'btc', 'service', NODE_LABELS.BtcIndexer) + constructor(endpoint: NodeInfo) { + super(endpoint, 'btc', 'service', NODE_LABELS.BtcIndexer) } protected buildClient(): AxiosInstance { @@ -18,9 +19,13 @@ export class BtcIndexer extends Node { protected async checkHealth() { const time = Date.now() - const blockNumber = await this.client.get('/blocks/tip/height').then((res) => { - return Number(res.data) || 0 - }) + const blockNumber = await this.client + .get('/blocks/tip/height', { + baseURL: this.preferAltIp ? this.altIp : this.url + }) + .then((res) => { + return Number(res.data) || 0 + }) return { height: Number(blockNumber), @@ -40,6 +45,7 @@ export class BtcIndexer extends Node { return this.client .request({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, url: path, method, params: method === 'GET' ? params : undefined, diff --git a/src/lib/nodes/btc-indexer/BtcIndexerClient.ts b/src/lib/nodes/btc-indexer/BtcIndexerClient.ts index 46d200cb6..12a5f683f 100644 --- a/src/lib/nodes/btc-indexer/BtcIndexerClient.ts +++ b/src/lib/nodes/btc-indexer/BtcIndexerClient.ts @@ -1,5 +1,6 @@ import type { AxiosRequestConfig } from 'axios' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { Client } from '../abstract.client' import { BtcIndexer } from './BtcIndexer' import { MULTIPLIER, normalizeTransaction } from './utils' @@ -17,7 +18,7 @@ import { GetUnspentsParams } from './types/api/get-unspents/get-unspents-params' * is not available at the moment. */ export class BtcIndexerClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('btc', 'service', NODE_LABELS.BtcIndexer) this.nodes = endpoints.map((endpoint) => new BtcIndexer(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/btc-indexer/index.ts b/src/lib/nodes/btc-indexer/index.ts index 1ceeed53d..6241994c6 100644 --- a/src/lib/nodes/btc-indexer/index.ts +++ b/src/lib/nodes/btc-indexer/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { BtcIndexerClient } from './BtcIndexerClient' -const endpoints = (config.btc.services.btcIndexer.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.btc.services.btcIndexer.list as NodeInfo[] + export const btcIndexer = new BtcIndexerClient(endpoints) export default btcIndexer diff --git a/src/lib/nodes/btc/BtcClient.ts b/src/lib/nodes/btc/BtcClient.ts index 0e8bf6df0..8f679f1f7 100644 --- a/src/lib/nodes/btc/BtcClient.ts +++ b/src/lib/nodes/btc/BtcClient.ts @@ -1,5 +1,6 @@ import type { AxiosRequestConfig } from 'axios' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { BtcNode } from './BtcNode' import { Client } from '../abstract.client' import { RpcRequest } from './types/api/common' @@ -12,7 +13,7 @@ import { RpcRequest } from './types/api/common' * is not available at the moment. */ export class BtcClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('btc', 'node', NODE_LABELS.BtcNode) this.nodes = endpoints.map((endpoint) => new BtcNode(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/btc/BtcNode.ts b/src/lib/nodes/btc/BtcNode.ts index ab0862997..4f61e9df8 100644 --- a/src/lib/nodes/btc/BtcNode.ts +++ b/src/lib/nodes/btc/BtcNode.ts @@ -3,6 +3,7 @@ import { createBtcLikeClient } from '../utils/createBtcLikeClient' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' import { formatBtcVersion } from '@/lib/nodes/utils/nodeVersionFormatters' +import type { NodeInfo } from '@/types/wallets' import { RpcRequest, RpcResponse } from './types/api/common' import { NetworkInfo } from './types/api/network-info' import { BlockchainInfo } from './types/api/blockchain-info' @@ -12,8 +13,8 @@ import { BlockchainInfo } from './types/api/blockchain-info' * to the node and verify is status (online/offline, version, ping, etc.) */ export class BtcNode extends Node { - constructor(url: string) { - super(url, 'btc', 'node', NODE_LABELS.BtcNode) + constructor(endpoint: NodeInfo) { + super(endpoint, 'btc', 'node', NODE_LABELS.BtcNode) } protected buildClient(): AxiosInstance { @@ -53,6 +54,7 @@ export class BtcNode extends Node { return this.client .request>({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, method: 'POST', data: params }) diff --git a/src/lib/nodes/btc/index.ts b/src/lib/nodes/btc/index.ts index 9354436e6..231351a48 100644 --- a/src/lib/nodes/btc/index.ts +++ b/src/lib/nodes/btc/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { BtcClient } from './BtcClient' -const endpoints = (config.btc.nodes.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.btc.nodes.list as NodeInfo[] + export const btc = new BtcClient(endpoints) export default btc diff --git a/src/lib/nodes/btc/types/api/common.ts b/src/lib/nodes/btc/types/api/common.ts index c79227127..ce4351c91 100644 --- a/src/lib/nodes/btc/types/api/common.ts +++ b/src/lib/nodes/btc/types/api/common.ts @@ -23,6 +23,7 @@ type Method = | 'getaddressbalance' export type RpcRequest

= { + baseURL?: string method: M params?: P } diff --git a/src/lib/nodes/dash/DashClient.ts b/src/lib/nodes/dash/DashClient.ts index 5f6362da3..aefbb62f3 100644 --- a/src/lib/nodes/dash/DashClient.ts +++ b/src/lib/nodes/dash/DashClient.ts @@ -1,5 +1,6 @@ import { AxiosRequestConfig } from 'axios' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { DashNode } from './DashNode' import { Client } from '../abstract.client' import { normalizeTransaction } from './utils' @@ -9,7 +10,7 @@ import { Balance } from './types/api/balance' import { Transaction } from './types/api/transaction' export class DashClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('dash', 'node', NODE_LABELS.DashNode) this.nodes = endpoints.map((endpoint) => new DashNode(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/dash/DashNode.ts b/src/lib/nodes/dash/DashNode.ts index a4fa154c0..e48bb850f 100644 --- a/src/lib/nodes/dash/DashNode.ts +++ b/src/lib/nodes/dash/DashNode.ts @@ -2,6 +2,7 @@ import { AxiosInstance, AxiosRequestConfig } from 'axios' import { createBtcLikeClient } from '../utils/createBtcLikeClient' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { RpcRequest, RpcResponse } from './types/api/common' import { NetworkInfo } from './types/api/network-info' import { BlockchainInfo } from './types/api/blockchain-info' @@ -11,8 +12,8 @@ import { BlockchainInfo } from './types/api/blockchain-info' * to the node and verify is status (online/offline, version, ping, etc.) */ export class DashNode extends Node { - constructor(url: string) { - super(url, 'dash', 'node', NODE_LABELS.DashNode) + constructor(endpoint: NodeInfo) { + super(endpoint, 'dash', 'node', NODE_LABELS.DashNode) } protected buildClient(): AxiosInstance { @@ -51,6 +52,7 @@ export class DashNode extends Node { return this.client .request>({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, url: '/', method: 'POST', data: params @@ -73,6 +75,7 @@ export class DashNode extends Node { return this.client .request[]>({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, url: '/', method: 'POST', data: params diff --git a/src/lib/nodes/dash/index.ts b/src/lib/nodes/dash/index.ts index 297b27701..5ae703ecc 100644 --- a/src/lib/nodes/dash/index.ts +++ b/src/lib/nodes/dash/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { DashClient } from './DashClient' -const endpoints = (config.dash.nodes.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.dash.nodes.list as NodeInfo[] + export const dash = new DashClient(endpoints) export default dash diff --git a/src/lib/nodes/dash/types/api/common.ts b/src/lib/nodes/dash/types/api/common.ts index c79227127..ce4351c91 100644 --- a/src/lib/nodes/dash/types/api/common.ts +++ b/src/lib/nodes/dash/types/api/common.ts @@ -23,6 +23,7 @@ type Method = | 'getaddressbalance' export type RpcRequest

= { + baseURL?: string method: M params?: P } diff --git a/src/lib/nodes/doge-indexer/DogeIndexer.ts b/src/lib/nodes/doge-indexer/DogeIndexer.ts index 212758b6e..684b63fc8 100644 --- a/src/lib/nodes/doge-indexer/DogeIndexer.ts +++ b/src/lib/nodes/doge-indexer/DogeIndexer.ts @@ -2,14 +2,15 @@ import { createBtcLikeClient } from '../utils/createBtcLikeClient' import type { AxiosInstance, AxiosRequestConfig } from 'axios' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' /** * Encapsulates a node. Provides methods to send API-requests * to the node and verify is status (online/offline, version, ping, etc.) */ export class DogeIndexer extends Node { - constructor(url: string) { - super(url, 'doge', 'service', NODE_LABELS.DogeIndexer) + constructor(endpoint: NodeInfo) { + super(endpoint, 'doge', 'service', NODE_LABELS.DogeIndexer) } protected buildClient(): AxiosInstance { @@ -19,7 +20,9 @@ export class DogeIndexer extends Node { protected async checkHealth() { const time = Date.now() const height = await this.client - .get('/api/status') + .get('/api/status', { + baseURL: this.preferAltIp ? this.altIp : this.url + }) .then((res) => res.data.info.blocks) return { @@ -40,6 +43,7 @@ export class DogeIndexer extends Node { return this.client .request({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, url: path, method, params: method === 'GET' ? params : undefined, diff --git a/src/lib/nodes/doge-indexer/DogeIndexerClient.ts b/src/lib/nodes/doge-indexer/DogeIndexerClient.ts index a0575b860..8285e6da7 100644 --- a/src/lib/nodes/doge-indexer/DogeIndexerClient.ts +++ b/src/lib/nodes/doge-indexer/DogeIndexerClient.ts @@ -1,5 +1,6 @@ import type { AxiosRequestConfig } from 'axios' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { DogeIndexer } from './DogeIndexer' import { Client } from '../abstract.client' import { NB_BLOCKS } from './constants' @@ -12,7 +13,7 @@ import { EstimatedFee, GetEstimatedFeeParams } from './types/api/estimated-fee' import { Balance } from './types/api/balance' export class DogeIndexerClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('doge', 'service', NODE_LABELS.DogeIndexer) this.nodes = endpoints.map((endpoint) => new DogeIndexer(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/doge-indexer/index.ts b/src/lib/nodes/doge-indexer/index.ts index 3dc391486..376a559f0 100644 --- a/src/lib/nodes/doge-indexer/index.ts +++ b/src/lib/nodes/doge-indexer/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { DogeIndexerClient } from './DogeIndexerClient' -const endpoints = (config.doge.nodes.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.doge.nodes.list as NodeInfo[] + export const dogeIndexer = new DogeIndexerClient(endpoints) export default dogeIndexer diff --git a/src/lib/nodes/doge/DogeClient.ts b/src/lib/nodes/doge/DogeClient.ts index f900a3fa2..57fca4355 100644 --- a/src/lib/nodes/doge/DogeClient.ts +++ b/src/lib/nodes/doge/DogeClient.ts @@ -1,11 +1,12 @@ import { AxiosRequestConfig } from 'axios' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { RpcRequest } from './types/api/common' import { DogeNode } from './DogeNode' import { Client } from '../abstract.client' export class DogeClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('doge', 'node', NODE_LABELS.DogeNode) this.nodes = endpoints.map((endpoint) => new DogeNode(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/doge/DogeNode.ts b/src/lib/nodes/doge/DogeNode.ts index 418e895b1..af0b4ae16 100644 --- a/src/lib/nodes/doge/DogeNode.ts +++ b/src/lib/nodes/doge/DogeNode.ts @@ -3,6 +3,7 @@ import { createBtcLikeClient } from '../utils/createBtcLikeClient' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' import { formatDogeVersion } from '@/lib/nodes/utils/nodeVersionFormatters' +import type { NodeInfo } from '@/types/wallets' import { RpcRequest, RpcResponse } from './types/api/common' import { NetworkInfo } from './types/api/network-info' import { BlockchainInfo } from './types/api/blockchain-info' @@ -12,8 +13,8 @@ import { BlockchainInfo } from './types/api/blockchain-info' * to the node and verify is status (online/offline, version, ping, etc.) */ export class DogeNode extends Node { - constructor(url: string) { - super(url, 'doge', 'node', NODE_LABELS.DogeNode) + constructor(endpoint: NodeInfo) { + super(endpoint, 'doge', 'node', NODE_LABELS.DogeNode) } protected buildClient(): AxiosInstance { @@ -55,6 +56,7 @@ export class DogeNode extends Node { return this.client .request>({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, url: '/', method: 'POST', data: params diff --git a/src/lib/nodes/doge/index.ts b/src/lib/nodes/doge/index.ts index c43df54ae..d14420456 100644 --- a/src/lib/nodes/doge/index.ts +++ b/src/lib/nodes/doge/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { DogeClient } from './DogeClient' -const endpoints = (config.doge.nodes.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.doge.nodes.list as NodeInfo[] + export const doge = new DogeClient(endpoints) export default doge diff --git a/src/lib/nodes/eth-indexer/EthIndexer.ts b/src/lib/nodes/eth-indexer/EthIndexer.ts index 6a709ce0b..110b7ad03 100644 --- a/src/lib/nodes/eth-indexer/EthIndexer.ts +++ b/src/lib/nodes/eth-indexer/EthIndexer.ts @@ -2,14 +2,15 @@ import { Endpoints } from './types/api/endpoints' import axios, { AxiosInstance, AxiosRequestConfig } from 'axios' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' /** * ETH Indexer API * https://github.com/Adamant-im/ETH-transactions-storage */ export class EthIndexer extends Node { - constructor(url: string) { - super(url, 'eth', 'service', NODE_LABELS.EthIndexer) + constructor(endpoint: NodeInfo) { + super(endpoint, 'eth', 'service', NODE_LABELS.EthIndexer) } protected buildClient(): AxiosInstance { @@ -30,6 +31,7 @@ export class EthIndexer extends Node { return this.client .request({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, url: path, method, params: method === 'GET' ? params : undefined, diff --git a/src/lib/nodes/eth-indexer/EthIndexerClient.ts b/src/lib/nodes/eth-indexer/EthIndexerClient.ts index b123f9acc..f47d65c31 100644 --- a/src/lib/nodes/eth-indexer/EthIndexerClient.ts +++ b/src/lib/nodes/eth-indexer/EthIndexerClient.ts @@ -1,5 +1,6 @@ import { AxiosRequestConfig } from 'axios' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { GetTransactionsParams } from './types/client/get-transactions-params' import { GetTransactionsRequest } from './types/api/get-transactions/get-transactions.request' import { Endpoints } from './types/api/endpoints' @@ -8,7 +9,7 @@ import { normalizeTransaction } from './utils' import { Client } from '../abstract.client' export class EthIndexerClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('eth', 'service', NODE_LABELS.EthIndexer) this.nodes = endpoints.map((endpoint) => new EthIndexer(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/eth-indexer/index.ts b/src/lib/nodes/eth-indexer/index.ts index 149d99eb0..0b047552d 100644 --- a/src/lib/nodes/eth-indexer/index.ts +++ b/src/lib/nodes/eth-indexer/index.ts @@ -1,10 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { EthIndexerClient } from './EthIndexerClient' -const endpoints = (config.eth.services.ethIndexer.list as NodeInfo[]).map( - (endpoint) => endpoint.url -) +const endpoints = config.eth.services.ethIndexer.list as NodeInfo[] + export const ethIndexer = new EthIndexerClient(endpoints) export default ethIndexer diff --git a/src/lib/nodes/eth/EthClient.ts b/src/lib/nodes/eth/EthClient.ts index 2eda5b184..78454b78a 100644 --- a/src/lib/nodes/eth/EthClient.ts +++ b/src/lib/nodes/eth/EthClient.ts @@ -4,6 +4,7 @@ import { NODE_LABELS } from '@/lib/nodes/constants' import { TransactionNotFound } from '@/lib/nodes/utils/errors' import { CryptoSymbol } from '@/lib/constants' import { bytesToHex } from '@/lib/hex' +import type { NodeInfo } from '@/types/wallets' import { EthNode } from './EthNode' import { Client } from '../abstract.client' import { normalizeEthTransaction, normalizeErc20Transaction } from './utils' @@ -16,7 +17,7 @@ import { normalizeEthTransaction, normalizeErc20Transaction } from './utils' * is not available at the moment. */ export class EthClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('eth', 'node', NODE_LABELS.EthNode) this.nodes = endpoints.map((endpoint) => new EthNode(endpoint)) this.minNodeVersion = minNodeVersion @@ -32,7 +33,7 @@ export class EthClient extends Client { const isFinalized = !!transaction.blockNumber return isFinalized - } catch (err) { + } catch { return false } } diff --git a/src/lib/nodes/eth/EthNode.ts b/src/lib/nodes/eth/EthNode.ts index cd349a96c..f7f9c7e14 100644 --- a/src/lib/nodes/eth/EthNode.ts +++ b/src/lib/nodes/eth/EthNode.ts @@ -3,6 +3,7 @@ import { HttpProvider } from 'web3-providers-http' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' import { formatEthVersion } from '@/lib/nodes/utils/nodeVersionFormatters' +import type { NodeInfo } from '@/types/wallets' /** * Encapsulates a node. Provides methods to send API-requests @@ -11,16 +12,20 @@ import { formatEthVersion } from '@/lib/nodes/utils/nodeVersionFormatters' export class EthNode extends Node { clientName = '' - constructor(url: string) { + constructor(url: NodeInfo) { super(url, 'eth', 'node', NODE_LABELS.EthNode) } protected buildClient(): Web3Eth { - return new Web3Eth(new HttpProvider(this.url)) + return new Web3Eth(new HttpProvider(this.preferAltIp ? (this.altIp as string) : this.url)) } protected async checkHealth() { const time = Date.now() + + /** Set `clientUrl` for `HttpProvider` depending on `preferAltIp`. */ + this.client = this.buildClient() + const blockNumber = await this.client.getBlockNumber() return { diff --git a/src/lib/nodes/eth/index.ts b/src/lib/nodes/eth/index.ts index 1f743b029..9bac6c2ec 100644 --- a/src/lib/nodes/eth/index.ts +++ b/src/lib/nodes/eth/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { EthClient } from './EthClient' -const endpoints = (config.eth.nodes.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.eth.nodes.list as NodeInfo[] + export const eth = new EthClient(endpoints) export default eth diff --git a/src/lib/nodes/ipfs/IpfsClient.ts b/src/lib/nodes/ipfs/IpfsClient.ts index ee3b6908f..15ce9d50a 100644 --- a/src/lib/nodes/ipfs/IpfsClient.ts +++ b/src/lib/nodes/ipfs/IpfsClient.ts @@ -1,6 +1,7 @@ import { isNodeOfflineError } from '@/lib/nodes/utils/errors' import { AxiosProgressEvent } from 'axios' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { IpfsNode, Payload, RequestConfig } from './IpfsNode' import { Client } from '../abstract.client' @@ -12,7 +13,7 @@ import { Client } from '../abstract.client' * is not available at the moment. */ export class IpfsClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('ipfs', 'service', NODE_LABELS.IpfsNode) this.nodes = endpoints.map((endpoint) => new IpfsNode(endpoint, minNodeVersion)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/ipfs/IpfsNode.ts b/src/lib/nodes/ipfs/IpfsNode.ts index fdfb845a5..ffdd4d9c1 100644 --- a/src/lib/nodes/ipfs/IpfsNode.ts +++ b/src/lib/nodes/ipfs/IpfsNode.ts @@ -3,6 +3,7 @@ import { NodeOfflineError } from '@/lib/nodes/utils/errors' import axios, { AxiosInstance, AxiosProgressEvent, AxiosRequestConfig, ResponseType } from 'axios' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' type FetchNodeInfoResult = { availableSizeInMb: number @@ -32,8 +33,8 @@ export type RequestConfig

= { * to the node and verify is status (online/offline, version, ping, etc.) */ export class IpfsNode extends Node { - constructor(url: string, minNodeVersion = '0.0.0') { - super(url, 'ipfs', 'node', NODE_LABELS.IpfsNode, '', minNodeVersion) + constructor(endpoint: NodeInfo, minNodeVersion = '0.0.0') { + super(endpoint, 'ipfs', 'node', NODE_LABELS.IpfsNode, '', minNodeVersion) } protected buildClient(): AxiosInstance { @@ -53,6 +54,7 @@ export class IpfsNode extends Node { const { url, headers, method = 'get', payload, onUploadProgress } = cfg const config: AxiosRequestConfig = { + baseURL: this.preferAltIp ? this.altIp : this.url, url, method: method.toLowerCase(), headers, @@ -77,7 +79,6 @@ export class IpfsNode extends Node { // According to https://github.com/axios/axios#handling-errors this means, that request was sent, // but server could not respond. if (!error.response && error.request) { - this.online = false throw new NodeOfflineError() } throw error @@ -117,7 +118,7 @@ export class IpfsNode extends Node { protected async checkHealth() { const time = Date.now() const { timestamp } = await this.fetchNodeInfo() - this.height = timestamp; + this.height = timestamp return { height: this.height, @@ -127,9 +128,10 @@ export class IpfsNode extends Node { formatHeight(height: number): string { return super.formatHeight( - Number(Math.ceil(height / 1000) - .toString() - .substring(2) + Number( + Math.ceil(height / 1000) + .toString() + .substring(2) ) ) } diff --git a/src/lib/nodes/ipfs/index.ts b/src/lib/nodes/ipfs/index.ts index affa21800..507a1c29b 100644 --- a/src/lib/nodes/ipfs/index.ts +++ b/src/lib/nodes/ipfs/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { IpfsClient } from './IpfsClient' -const endpoints = (config.adm.services.ipfsNode.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.adm.services.ipfsNode.list as NodeInfo[] + export const ipfs = new IpfsClient(endpoints, config.adm.services.minVersion) export default ipfs diff --git a/src/lib/nodes/kly-indexer/KlyIndexer.ts b/src/lib/nodes/kly-indexer/KlyIndexer.ts index 143482e33..a5a909f55 100644 --- a/src/lib/nodes/kly-indexer/KlyIndexer.ts +++ b/src/lib/nodes/kly-indexer/KlyIndexer.ts @@ -1,6 +1,7 @@ import axios, { AxiosInstance, AxiosRequestConfig } from 'axios' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import type { NodeInfo } from '@/types/wallets' import { Endpoints } from './types/api/endpoints' /** @@ -8,8 +9,8 @@ import { Endpoints } from './types/api/endpoints' * to the node and verify is status (online/offline, version, ping, etc.) */ export class KlyIndexer extends Node { - constructor(url: string) { - super(url, 'kly', 'service', NODE_LABELS.KlyIndexer) + constructor(endpoint: NodeInfo) { + super(endpoint, 'kly', 'service', NODE_LABELS.KlyIndexer) } protected buildClient(): AxiosInstance { @@ -31,6 +32,7 @@ export class KlyIndexer extends Node { return this.client .request({ ...requestConfig, + baseURL: this.preferAltIp ? this.altIp : this.url, url: path, method, params: method === 'GET' ? params : undefined, diff --git a/src/lib/nodes/kly-indexer/KlyIndexerClient.ts b/src/lib/nodes/kly-indexer/KlyIndexerClient.ts index 7806a2c28..960eb0cd1 100644 --- a/src/lib/nodes/kly-indexer/KlyIndexerClient.ts +++ b/src/lib/nodes/kly-indexer/KlyIndexerClient.ts @@ -2,6 +2,7 @@ import { AxiosRequestConfig } from 'axios' import { KLY_TOKEN_ID } from '@/lib/klayr' import { NODE_LABELS } from '@/lib/nodes/constants' import { TransactionNotFound } from '@/lib/nodes/utils/errors' +import type { NodeInfo } from '@/types/wallets' import { normalizeTransaction } from './utils' import { TransactionParams } from './types/api/transactions/transaction-params' import { Endpoints } from './types/api/endpoints' @@ -9,7 +10,7 @@ import { KlyIndexer } from './KlyIndexer' import { Client } from '../abstract.client' export class KlyIndexerClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('kly', 'service', NODE_LABELS.KlyIndexer) this.nodes = endpoints.map((endpoint) => new KlyIndexer(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/kly-indexer/index.ts b/src/lib/nodes/kly-indexer/index.ts index d791926f1..2617c7e41 100644 --- a/src/lib/nodes/kly-indexer/index.ts +++ b/src/lib/nodes/kly-indexer/index.ts @@ -1,10 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { KlyIndexerClient } from './KlyIndexerClient' -const endpoints = (config.kly.services.klyService.list as NodeInfo[]).map( - (endpoint) => endpoint.url -) +const endpoints = config.kly.services.klyService.list as NodeInfo[] + export const klyIndexer = new KlyIndexerClient(endpoints) export default klyIndexer diff --git a/src/lib/nodes/kly/KlyClient.ts b/src/lib/nodes/kly/KlyClient.ts index d58b66bf9..e23bf0886 100644 --- a/src/lib/nodes/kly/KlyClient.ts +++ b/src/lib/nodes/kly/KlyClient.ts @@ -1,12 +1,13 @@ import { convertBeddowsTokly } from '@klayr/transactions' import { NODE_LABELS } from '@/lib/nodes/constants' import { KLY_TOKEN_ID } from '@/lib/klayr' +import type { NodeInfo } from '@/types/wallets' import { RpcMethod, RpcResults } from './types/api' import { KlyNode } from './KlyNode' import { Client } from '../abstract.client' export class KlyClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('kly', 'node', NODE_LABELS.KlyNode) this.nodes = endpoints.map((endpoint) => new KlyNode(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/kly/KlyNode.ts b/src/lib/nodes/kly/KlyNode.ts index 586abcd42..3c38b6fdc 100644 --- a/src/lib/nodes/kly/KlyNode.ts +++ b/src/lib/nodes/kly/KlyNode.ts @@ -3,6 +3,7 @@ import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' import { RpcMethod, RpcResults } from './types/api' import { JSONRPCResponse } from '@/lib/klayr' +import type { NodeInfo } from '@/types/wallets' import { v4 as uuid } from 'uuid' /** @@ -10,8 +11,8 @@ import { v4 as uuid } from 'uuid' * to the node and verify is status (online/offline, version, ping, etc.) */ export class KlyNode extends Node { - constructor(url: string) { - super(url, 'kly', 'node', NODE_LABELS.KlyNode) + constructor(endpoint: NodeInfo) { + super(endpoint, 'kly', 'node', NODE_LABELS.KlyNode) } protected buildClient(): AxiosInstance { @@ -29,6 +30,7 @@ export class KlyNode extends Node { ): Promise { return this.client .post>('/rpc', { + baseURL: this.preferAltIp ? this.altIp : this.url, jsonrpc: '2.0', id: uuid(), method, diff --git a/src/lib/nodes/kly/index.ts b/src/lib/nodes/kly/index.ts index eaf6b6c86..e67cda5f9 100644 --- a/src/lib/nodes/kly/index.ts +++ b/src/lib/nodes/kly/index.ts @@ -1,8 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' +import type { NodeInfo } from '@/types/wallets' import { KlyClient } from './KlyClient' -const endpoints = (config.kly.nodes.list as NodeInfo[]).map((endpoint) => endpoint.url) +const endpoints = config.kly.nodes.list as NodeInfo[] + export const kly = new KlyClient(endpoints) export default kly diff --git a/src/lib/nodes/rate-info-service/RateInfoClient.ts b/src/lib/nodes/rate-info-service/RateInfoClient.ts index a19084496..dbd4bb079 100644 --- a/src/lib/nodes/rate-info-service/RateInfoClient.ts +++ b/src/lib/nodes/rate-info-service/RateInfoClient.ts @@ -2,9 +2,10 @@ import { NODE_LABELS } from '@/lib/nodes/constants' import { Client } from '@/lib/nodes/abstract.client' import { RateInfoService } from '@/lib/nodes/rate-info-service/RateInfoService' import { RateInfoResponse } from '@/lib/nodes/rate-info-service/types/RateInfoResponse' +import type { NodeInfo } from '@/types/wallets' export class RateInfoClient extends Client { - constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + constructor(endpoints: NodeInfo[] = [], minNodeVersion = '0.0.0') { super('adm', 'service', NODE_LABELS.RatesInfo) this.nodes = endpoints.map((endpoint) => new RateInfoService(endpoint)) this.minNodeVersion = minNodeVersion diff --git a/src/lib/nodes/rate-info-service/RateInfoService.ts b/src/lib/nodes/rate-info-service/RateInfoService.ts index e5e026ac2..f70d34131 100644 --- a/src/lib/nodes/rate-info-service/RateInfoService.ts +++ b/src/lib/nodes/rate-info-service/RateInfoService.ts @@ -5,11 +5,13 @@ import { NODE_LABELS } from '@/lib/nodes/constants' import { RateInfoResponse } from '@/lib/nodes/rate-info-service/types/RateInfoResponse' import { RateHistoryInfoResponse } from '@/lib/nodes/rate-info-service/types/RateHistoryInfoResponse' import { GetHistoryParams } from '@/lib/nodes/rate-info-service/types/GetHistoryParams' +import type { NodeInfo } from '@/types/wallets' export class RateInfoService extends Node { - constructor(url: string) { - super(url, 'adm', 'service', NODE_LABELS.RatesInfo) + constructor(endpoint: NodeInfo) { + super(endpoint, 'adm', 'service', NODE_LABELS.RatesInfo) } + protected buildClient(): AxiosInstance { return axios.create({ baseURL: this.url @@ -17,14 +19,19 @@ export class RateInfoService extends Node { } async getAllRates(): Promise { - const response = await this.client.get('/get') + const response = await this.client.get('/get', { + baseURL: this.preferAltIp ? this.altIp : this.url + }) + return response.data } async getHistory(options: GetHistoryParams) { const response = await this.client.get(`/getHistory`, { + baseURL: this.preferAltIp ? this.altIp : this.url, params: options }) + return response.data } @@ -41,9 +48,10 @@ export class RateInfoService extends Node { formatHeight(height: number): string { return super.formatHeight( - Number(Math.ceil(height / 1000) - .toString() - .substring(2) + Number( + Math.ceil(height / 1000) + .toString() + .substring(2) ) ) } diff --git a/src/lib/nodes/rate-info-service/index.ts b/src/lib/nodes/rate-info-service/index.ts index a5e8d965b..a9a7c1506 100644 --- a/src/lib/nodes/rate-info-service/index.ts +++ b/src/lib/nodes/rate-info-service/index.ts @@ -1,10 +1,9 @@ import config from '@/config' -import { NodeInfo } from '@/types/wallets' import { RateInfoClient } from '@/lib/nodes/rate-info-service/RateInfoClient' +import type { NodeInfo } from '@/types/wallets' + +const endpoints = config.adm.services.infoService.list as NodeInfo[] -const endpoints = (config.adm.services.infoService.list as NodeInfo[]).map( - (endpoint) => endpoint.url -) export const rateInfoClient = new RateInfoClient(endpoints) export default rateInfoClient