From dd9a3051a331334654c5e68733366e77163a6fad Mon Sep 17 00:00:00 2001 From: Aleksandr Kraiz Date: Tue, 17 May 2022 22:06:30 +0400 Subject: [PATCH] New init / new config / options --- README.md | 21 +++-- package.json | 2 +- src/OrionUnit/index.ts | 93 ++++++++++++++++--- src/config/chains.json | 2 +- src/config/envs.json | 32 +++---- src/getOrionUnitSiblings.ts | 4 +- src/initOrionUnit.ts | 53 +---------- src/services/OrionAggregator/index.ts | 8 +- src/services/OrionAnalytics/index.ts | 2 +- src/services/OrionBlockchain/index.ts | 66 ++++++------- src/services/PriceFeed/index.ts | 24 ++++- .../PriceFeed/ws/PriceFeedTickerWS.ts | 5 +- 12 files changed, 179 insertions(+), 133 deletions(-) diff --git a/README.md b/README.md index 5b89145..4437220 100644 --- a/README.md +++ b/README.md @@ -18,38 +18,38 @@ npm i @orionprotocol/sdk ```ts // Node.js import "dotenv/config"; -import { initOrionUnit } from "@orionprotocol/sdk"; +import { OrionUnit } from "@orionprotocol/sdk"; import { Wallet } from "ethers"; -const chain = process.env.CHAINID; // "56" +const chain = process.env.CHAIN; // "56" or "bsc" const env = process.env.ENV; // production const privateKey = process.env.PRIVATE_KEY; // 0x... -if (!chain) throw new Error("CHAINID is required"); +if (!chain) throw new Error("CHAIN is required"); if (!env) throw new Error("ENV is required"); if (!privateKey) throw new Error("PRIVATE_KEY is required"); const wallet = new Wallet(privateKey); // OrionUnit is chain-in-environment abstraction -const orionUnit = initOrionUnit(chain, env); +const orionUnit = new OrionUnit(chain, env); ``` ```ts // UI -import { initOrionUnit } from "@orionprotocol/sdk"; +import { OrionUnit } from "@orionprotocol/sdk"; import detectEthereumProvider from "@metamask/detect-provider"; import { BaseProvider } from "@metamask/providers"; import { providers } from "ethers"; -const chain = "97"; // bsc-testnet +const chain = "97"; // or "bsc" (in testing environment point to bsc-testnet) const env = "testing"; const startApp = async (provider: BaseProvider) => { const web3Provider = new providers.Web3Provider(provider); await web3Provider.ready; const signer = web3Provider.getSigner(); // ready to go - const orionUnit = initOrionUnit(chain, env); // ready to go + const orionUnit = new OrionUnit(chain, env); // ready to go }; detectEthereumProvider().then((provider) => { @@ -211,9 +211,10 @@ const { orderId } = await simpleFetch(orionUnit.orionAggregator.placeOrder)( // Default ("verbose") fetch -const placeOrderFetchResult = await orionUnit.orionAggregator.placeOrder( -// Same params as above -); +const placeOrderFetchResult = await orionUnit.orionAggregator + .placeOrder + // Same params as above + (); if (placeOrderFetchResult.isErr()) { // You can handle fetching errors here diff --git a/package.json b/package.json index ac89409..129406e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orionprotocol/sdk", - "version": "0.5.20", + "version": "0.6.0", "description": "Orion Protocol SDK", "main": "./lib/esm/index.js", "module": "./lib/esm/index.js", diff --git a/src/OrionUnit/index.ts b/src/OrionUnit/index.ts index 0a228c5..652ff11 100644 --- a/src/OrionUnit/index.ts +++ b/src/OrionUnit/index.ts @@ -7,8 +7,25 @@ import { PriceFeed } from '../services/PriceFeed'; import { SupportedChainId } from '../types'; import Exchange from './Exchange'; import FarmingManager from './FarmingManager'; +import { chains, envs } from '../config'; +import { isValidChainId } from '../utils'; -const orionAnalyticsHost = 'trade.orionprotocol.io'; +const orionAnalyticsUrl = 'https://trade.orionprotocol.io'; + +type Options = { + api?: string; + services?: { + orionBlockchain?: { + api?: string; + }, + orionAggregator?: { + api?: string; + }, + priceFeed?: { + api?: string; + }, + } +}; export default class OrionUnit { public readonly env: string; @@ -33,22 +50,76 @@ export default class OrionUnit { public readonly apiUrl: string; constructor( - chainId: SupportedChainId, - networkCode: string, - rpc: string, + chain: string, env: string, - apiUrl: string, + options?: Options, ) { + if (!(env in envs)) { + throw new Error(`Env '${env}' not found. Available environments is: ${Object.keys(envs).join(', ')}`); + } + + const envInfo = envs[env]; + const envNetworks = envInfo?.networks; + let chainId: SupportedChainId; + + if (isValidChainId(chain)) chainId = chain; + else { + const targetChains = Object + .keys(chains) + .filter(isValidChainId) + .filter((ch) => { + const chainInfo = chains[ch]; + if (!chainInfo) return false; + return (chainInfo.chainId in envNetworks) + && (chainInfo.code.toLowerCase() === chain.toLowerCase()); + }); + if (targetChains.length !== 1) { + throw new Error( + targetChains.length > 1 + ? 'Ambiguation detected. ' + + `Found ${targetChains.length} chain ids [${targetChains.join(', ')}] for chain name '${chain}' in env '${env}'. Expected 1.` + : `Chains not found for chain name '${chain}' in env '${env}'.`, + ); + } + [chainId] = targetChains; + } + + if (!(chainId in envNetworks)) { + throw new Error(`Chain '${chainId}' not found. ` + + `Available chains in selected environment (${env}) is: ${Object.keys(envNetworks).join(', ')}`); + } + + const envNetworkInfo = envNetworks[chainId]; + const chainInfo = chains[chainId]; + + if (!envNetworkInfo) throw new Error('Env network info is required'); + if (!chainInfo) throw new Error('Chain info is required'); + + const apiUrl = envNetworkInfo.api; + this.chainId = chainId; - this.networkCode = networkCode; - this.provider = new ethers.providers.StaticJsonRpcProvider(rpc); + this.networkCode = chainInfo.code; + this.provider = new ethers.providers.StaticJsonRpcProvider(envNetworkInfo.rpc ?? chainInfo.rpc); this.env = env; this.apiUrl = apiUrl; - this.orionBlockchain = new OrionBlockchain(apiUrl); - this.orionAggregator = new OrionAggregator(apiUrl, chainId); - this.priceFeed = new PriceFeed(apiUrl); - this.orionAnalytics = new OrionAnalytics(orionAnalyticsHost); + this.orionBlockchain = new OrionBlockchain( + options?.services?.orionBlockchain?.api + ?? options?.api + ?? apiUrl, + ); + this.orionAggregator = new OrionAggregator( + options?.services?.orionAggregator?.api + ?? options?.api + ?? apiUrl, + chainId, + ); + this.priceFeed = new PriceFeed( + options?.services?.priceFeed?.api + ?? options?.api + ?? apiUrl, + ); + this.orionAnalytics = new OrionAnalytics(orionAnalyticsUrl); this.exchange = new Exchange(this); this.farmingManager = new FarmingManager(this); } diff --git a/src/config/chains.json b/src/config/chains.json index 55d9b08..5adc9e5 100644 --- a/src/config/chains.json +++ b/src/config/chains.json @@ -21,7 +21,7 @@ "chainId": "97", "explorer": "https://testnet.bscscan.com/", "label": "Binance Smart Chain Testnet", - "shortName": "BSC-Testent", + "shortName": "BSC-Testnet", "code": "bsc", "rpc": "https://bsc-stage.node.orionprotocol.io/", "baseCurrencyName": "BNB" diff --git a/src/config/envs.json b/src/config/envs.json index 6f636c4..b202a8d 100644 --- a/src/config/envs.json +++ b/src/config/envs.json @@ -2,73 +2,73 @@ "production": { "networks": { "1": { - "api": "trade.orionprotocol.io", + "api": "https://trade.orionprotocol.io", "liquidityMigratorAddress": "0x23a1820a47BcD022E29f6058a5FD224242F50D1A" }, "56": { - "api": "trade-exp.orionprotocol.io" + "api": "https://trade-exp.orionprotocol.io" }, "250": { - "api": "trade-ftm.orionprotocol.io" + "api": "https://trade-ftm.orionprotocol.io" } } }, "testing": { "networks": { "97": { - "api": "testing.orionprotocol.io/bsc-testnet", + "api": "https://testing.orionprotocol.io/bsc-testnet", "liquidityMigratorAddress": "0x01b10dds12478C88A5E18e2707E729906bC25CfF6" }, "3": { - "api": "testing.orionprotocol.io/eth-ropsten", + "api": "https://testing.orionprotocol.io/eth-ropsten", "liquidityMigratorAddress": "0x36969a25622AE31bA9946e0c8151f0dc08b3A1c8" }, "4002": { - "api": "testing.orionprotocol.io/ftm-testnet" + "api": "https://testing.orionprotocol.io/ftm-testnet" } } }, "staging": { "networks": { "1": { - "api": "staging.orionprotocol.io" + "api": "https://staging.orionprotocol.io" }, "56": { - "api": "staging-bsc.orionprotocol.io" + "api": "https://staging-bsc.orionprotocol.io" }, "250": { - "api": "staging-ftm.orionprotocol.io" + "api": "https://staging-ftm.orionprotocol.io" }, "137": { - "api": "staging-polygon.orionprotocol.io" + "api": "https://staging-polygon.orionprotocol.io" } } }, "broken": { "networks": { "0": { - "api": "broken0.orionprotocol.io/everything-is-fine/this-url-was-created-for-tests-of-a-partially-broken-environment" + "api": "https://broken0.orionprotocol.io/everything-is-fine/this-url-was-created-for-tests-of-a-partially-broken-environment" }, "97": { - "api": "broken1.orionprotocol.io/everything-is-fine/this-url-was-created-for-tests-of-a-partially-broken-environment" + "api": "https://broken1.orionprotocol.io/everything-is-fine/this-url-was-created-for-tests-of-a-partially-broken-environment" } } }, "partially-broken": { "networks": { "97": { - "api": "testing.orionprotocol.io/bsc-testnet", + "api": "https://testing.orionprotocol.io/bsc-testnet", "liquidityMigratorAddress": "0x01b10dd12478C88A5E18e2707E729906bC25CfF6" }, "3": { - "api": "testing.orionprotocol.io/eth-ropsten", + "api": "https://testing.orionprotocol.io/eth-ropsten", "liquidityMigratorAddress": "0x36969a25622AE31bA9946e0c8151f0dc08b3A1c8" }, "4002": { - "api": "testing.orionprotocol.io/ftm-testnet" + "api": "https://testing.orionprotocol.io/ftm-testnet" }, "0": { - "api": "broken.orionprotocol.io/everything-is-fine/this-url-was-created-for-tests-of-a-partially-broken-environment" + "api": "https://broken.orionprotocol.io/everything-is-fine/this-url-was-created-for-tests-of-a-partially-broken-environment" } } } diff --git a/src/getOrionUnitSiblings.ts b/src/getOrionUnitSiblings.ts index 4a749ab..1e2a3a1 100644 --- a/src/getOrionUnitSiblings.ts +++ b/src/getOrionUnitSiblings.ts @@ -1,7 +1,7 @@ -import initOrionUnit from './initOrionUnit'; import { SupportedChainId } from './types'; import { isValidChainId } from './utils'; import { envs } from './config'; +import OrionUnit from './OrionUnit'; export default function getOrionUnitSiblings(siblingChain: SupportedChainId, env: string) { if (!(env in envs)) throw new Error(`Env '${env}' not found. Available environments is: ${Object.keys(envs).join(', ')}`); @@ -16,5 +16,5 @@ export default function getOrionUnitSiblings(siblingChain: SupportedChainId, env .keys(envNetworks) .filter(isValidChainId) .filter((chainId) => chainId !== siblingChain); - return siblingsNetworks.map((chainId) => initOrionUnit(chainId, env)); + return siblingsNetworks.map((chainId) => new OrionUnit(chainId, env)); } diff --git a/src/initOrionUnit.ts b/src/initOrionUnit.ts index bf190cf..df19020 100644 --- a/src/initOrionUnit.ts +++ b/src/initOrionUnit.ts @@ -1,55 +1,8 @@ import OrionUnit from './OrionUnit'; -import { isValidChainId } from './utils'; -import { chains, envs } from './config'; -import { SupportedChainId } from './types'; - -export default function initOrionUnit(chain: string, env: string) { - if (!(env in envs)) { - throw new Error(`Env '${env}' not found. Available environments is: ${Object.keys(envs).join(', ')}`); - } - - const envInfo = envs[env]; - const envNetworks = envInfo?.networks; - let chainId: SupportedChainId; - - if (isValidChainId(chain)) chainId = chain; - else { - const targetChains = Object - .keys(chains) - .filter(isValidChainId) - .filter((ch) => { - const chainInfo = chains[ch]; - if (!chainInfo) return false; - return (chainInfo.chainId in envNetworks) - && (chainInfo.code.toLowerCase() === chain.toLowerCase()); - }); - if (targetChains.length !== 1) { - throw new Error( - targetChains.length > 1 - ? 'Ambiguation detected. ' - + `Found ${targetChains.length} chain ids [${targetChains.join(', ')}] for chain name '${chain}' in env '${env}'. Expected 1.` - : `Chains not found for chain name '${chain}' in env '${env}'.`, - ); - } - [chainId] = targetChains; - } - - if (!(chainId in envNetworks)) { - throw new Error(`Chain '${chainId}' not found. ` - + `Available chains in selected environment (${env}) is: ${Object.keys(envNetworks).join(', ')}`); - } - - const envNetworkInfo = envNetworks[chainId]; - const chainInfo = chains[chainId]; - - if (!envNetworkInfo) throw new Error('Env network info is required'); - if (!chainInfo) throw new Error('Chain info is required'); +// backward compatibility +export default function initOrionUnit(...params: ConstructorParameters) { return new OrionUnit( - chainId, - chainInfo.code, - envNetworkInfo.rpc ?? chainInfo.rpc, - env, - envNetworkInfo.api, + ...params, ); } diff --git a/src/services/OrionAggregator/index.ts b/src/services/OrionAggregator/index.ts index 3259f81..9e4663a 100644 --- a/src/services/OrionAggregator/index.ts +++ b/src/services/OrionAggregator/index.ts @@ -39,10 +39,14 @@ class OrionAggregator { this.getExchangeOrderbook = this.getExchangeOrderbook.bind(this); } - get aggregatorWSUrl() { return `wss://${this.apiUrl}/v1`; } + get aggregatorWSUrl() { + const { host, pathname, protocol } = new URL(this.apiUrl); + const wsProtocol = protocol === 'https:' ? 'wss' : 'ws'; + return `${wsProtocol}://${host + (pathname === '/' ? '' : pathname)}/v1`; + } get aggregatorUrl() { - return `https://${this.apiUrl}/backend`; + return `${this.apiUrl}/backend`; } getPairsList = () => fetchWithValidation( diff --git a/src/services/OrionAnalytics/index.ts b/src/services/OrionAnalytics/index.ts index 722b1f5..2370b55 100644 --- a/src/services/OrionAnalytics/index.ts +++ b/src/services/OrionAnalytics/index.ts @@ -11,7 +11,7 @@ export default class OrionAnalytics { } getOverview = () => fetchWithValidation( - `https://${this.apiUrl}/api/stats/overview`, + `${this.apiUrl}/api/stats/overview`, overviewSchema, ); } diff --git a/src/services/OrionBlockchain/index.ts b/src/services/OrionBlockchain/index.ts index f4093c4..96e2160 100644 --- a/src/services/OrionBlockchain/index.ts +++ b/src/services/OrionBlockchain/index.ts @@ -56,7 +56,7 @@ class OrionBlockchain { constructor(apiUrl: string) { this.apiUrl = apiUrl; - this.ws = new OrionBlockchainSocketIO(`https://${apiUrl}/`); + this.ws = new OrionBlockchainSocketIO(`${apiUrl}/`); this.getAtomicSwapAssets = this.getAtomicSwapAssets.bind(this); this.getAtomicSwapHistory = this.getAtomicSwapHistory.bind(this); @@ -89,11 +89,11 @@ class OrionBlockchain { } get orionBlockchainWsUrl() { - return `https://${this.apiUrl}/`; + return `${this.apiUrl}/`; } private getSummaryRedeem = (brokerAddress: string, unshifted?: 1 | 0, sourceNetworkCode?: string) => { - const url = new URL(`https://${this.apiUrl}/api/atomic/summary-redeem/${brokerAddress}`); + const url = new URL(`${this.apiUrl}/api/atomic/summary-redeem/${brokerAddress}`); if (unshifted) { url.searchParams.append('unshifted', unshifted.toString()); } @@ -107,12 +107,12 @@ class OrionBlockchain { }; private getSummaryClaim = (brokerAddress: string) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/summary-claim/${brokerAddress}`, + `${this.apiUrl}/api/atomic/summary-claim/${brokerAddress}`, atomicSummarySchema, ); private getQueueLength = () => fetchWithValidation( - `https://${this.apiUrl}/api/queueLength`, + `${this.apiUrl}/api/queueLength`, z.number().int(), ); @@ -125,54 +125,54 @@ class OrionBlockchain { } getAuthToken = () => fetchWithValidation( - `https://${this.apiUrl}/api/auth/token`, + `${this.apiUrl}/api/auth/token`, z.object({ token: z.string() }), ); getCirculatingSupply = () => fetchWithValidation( - `https://${this.apiUrl}/api/circulating-supply`, + `${this.apiUrl}/api/circulating-supply`, z.number(), ); - getInfo = () => fetchWithValidation(`https://${this.apiUrl}/api/info`, infoSchema); + getInfo = () => fetchWithValidation(`${this.apiUrl}/api/info`, infoSchema); getPoolsConfig = () => fetchWithValidation( - `https://${this.apiUrl}/api/pools/config`, + `${this.apiUrl}/api/pools/config`, poolsConfigSchema, ); getPoolsInfo = () => fetchWithValidation( - `https://${this.apiUrl}/api/pools/info`, + `${this.apiUrl}/api/pools/info`, poolsInfoSchema, ); getHistory = (address: string) => fetchWithValidation( - `https://${this.apiUrl}/api/history/${address}`, + `${this.apiUrl}/api/history/${address}`, historySchema, ); getPrices = () => fetchWithValidation( - `https://${this.apiUrl}/api/prices`, + `${this.apiUrl}/api/prices`, z.record(z.string()).transform(makePartial), ); getTokensFee = () => fetchWithValidation( - `https://${this.apiUrl}/api/tokensFee`, + `${this.apiUrl}/api/tokensFee`, z.record(z.string()).transform(makePartial), ); getGasPriceWei = () => fetchWithValidation( - `https://${this.apiUrl}/api/gasPrice`, + `${this.apiUrl}/api/gasPrice`, z.string(), ); checkFreeRedeemAvailable = (walletAddress: string) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/has-free-redeem/${walletAddress}`, + `${this.apiUrl}/api/atomic/has-free-redeem/${walletAddress}`, z.boolean(), ); getRedeemOrderBySecretHash = (secretHash: string) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/redeem-order/${secretHash}`, + `${this.apiUrl}/api/atomic/redeem-order/${secretHash}`, z.object({ secretHash: z.string(), secret: z.string(), @@ -181,7 +181,7 @@ class OrionBlockchain { ); claimOrder = (secretHash: string, targetNetwork: string, redeemTxHash?: string) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/claim-order`, + `${this.apiUrl}/api/atomic/claim-order`, z.string(), { method: 'POST', @@ -201,7 +201,7 @@ class OrionBlockchain { secret: string, sourceNetwork: string, ) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/matcher-redeem`, + `${this.apiUrl}/api/atomic/matcher-redeem`, z.string(), { method: 'POST', @@ -223,7 +223,7 @@ class OrionBlockchain { secret2: string, sourceNetwork: string, ) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/matcher-redeem2atomics`, + `${this.apiUrl}/api/atomic/matcher-redeem2atomics`, z.string(), { method: 'POST', @@ -241,31 +241,31 @@ class OrionBlockchain { ); checkRedeem = (secretHash: string) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/matcher-redeem/${secretHash}`, + `${this.apiUrl}/api/atomic/matcher-redeem/${secretHash}`, z.enum(['OK', 'FAIL']).nullable(), ); checkRedeem2Atomics = (firstSecretHash: string, secondSecretHash: string) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/matcher-redeem/${firstSecretHash}-${secondSecretHash}`, + `${this.apiUrl}/api/atomic/matcher-redeem/${firstSecretHash}-${secondSecretHash}`, z.enum(['OK', 'FAIL']).nullable(), ); - getBlockNumber = () => fetchWithValidation(`https://${this.apiUrl}/api/blocknumber`, z.number().int()); + getBlockNumber = () => fetchWithValidation(`${this.apiUrl}/api/blocknumber`, z.number().int()); - getIDOInfo = () => fetchWithValidation(`https://${this.apiUrl}/api/solarflare`, IDOSchema); + getIDOInfo = () => fetchWithValidation(`${this.apiUrl}/api/solarflare`, IDOSchema); - checkAuth = (headers: IAdminAuthHeaders) => fetchWithValidation(`https://${this.apiUrl}/api/auth/check`, z.object({ + checkAuth = (headers: IAdminAuthHeaders) => fetchWithValidation(`${this.apiUrl}/api/auth/check`, z.object({ auth: z.boolean(), }), { headers }); getPoolsList = (headers: IAdminAuthHeaders) => fetchWithValidation( - `https://${this.apiUrl}/api/pools/list`, + `${this.apiUrl}/api/pools/list`, adminPoolsListSchema, { headers }, ); editPool = (address: string, data: IEditPool, headers: IAdminAuthHeaders) => fetchWithValidation( - `https://${this.apiUrl}/api/pools/edit/${address}`, + `${this.apiUrl}/api/pools/edit/${address}`, pairStatusSchema, { method: 'POST', @@ -278,7 +278,7 @@ class OrionBlockchain { ); addPool = (data: z.infer) => fetchWithValidation( - `https://${this.apiUrl}/api/pools/add`, + `${this.apiUrl}/api/pools/add`, z.number(), { method: 'POST', @@ -292,12 +292,12 @@ class OrionBlockchain { ); checkPoolInformation = (poolAddress: string) => fetchWithValidation( - `https://${this.apiUrl}/api/pools/check/${poolAddress}`, + `${this.apiUrl}/api/pools/check/${poolAddress}`, pairStatusSchema, ); getAtomicSwapAssets = () => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/swap-assets`, + `${this.apiUrl}/api/atomic/swap-assets`, z.array(z.string()), ); @@ -306,7 +306,7 @@ class OrionBlockchain { * Receiver is user address in target Orion Blockchain instance */ getAtomicSwapHistory = (query: AtomicSwapHistorySourceQuery | AtomicSwapHistoryTargetQuery) => { - const url = new URL(`https://${this.apiUrl}/api/atomic/history/`); + const url = new URL(`${this.apiUrl}/api/atomic/history/`); Object.entries(query) .forEach(([key, value]) => url.searchParams.append(key, value.toString())); @@ -315,7 +315,7 @@ class OrionBlockchain { }; getSourceAtomicSwapHistory = (query: AtomicSwapHistorySourceQuery) => { - const url = new URL(`https://${this.apiUrl}/api/atomic/history/`); + const url = new URL(`${this.apiUrl}/api/atomic/history/`); Object.entries(query) .forEach(([key, value]) => url.searchParams.append(key, value.toString())); @@ -326,7 +326,7 @@ class OrionBlockchain { }; getTargetAtomicSwapHistory = (query: AtomicSwapHistoryTargetQuery) => { - const url = new URL(`https://${this.apiUrl}/api/atomic/history/`); + const url = new URL(`${this.apiUrl}/api/atomic/history/`); Object.entries(query) .forEach(([key, value]) => url.searchParams.append(key, value.toString())); @@ -337,7 +337,7 @@ class OrionBlockchain { }; checkIfHashUsed = (secretHashes: string[]) => fetchWithValidation( - `https://${this.apiUrl}/api/atomic/is-hash-used`, + `${this.apiUrl}/api/atomic/is-hash-used`, z.record(z.boolean()).transform(makePartial), { headers: { diff --git a/src/services/PriceFeed/index.ts b/src/services/PriceFeed/index.ts index 902d382..19cf7ca 100644 --- a/src/services/PriceFeed/index.ts +++ b/src/services/PriceFeed/index.ts @@ -17,7 +17,7 @@ class PriceFeed { interval: '5m' | '30m' | '1h' | '1d', exchange: string, ) => { - const url = new URL(`https://${this.apiUrl}/candles/candles`); + const url = new URL(`${this.apiUrl}/candles/candles`); url.searchParams.append('symbol', symbol); url.searchParams.append('timeStart', timeStart.toString()); url.searchParams.append('timeEnd', timeEnd.toString()); @@ -30,13 +30,27 @@ class PriceFeed { ); }; - get candlesUrl() { return `https://${this.apiUrl}/candles/candles`; } + get wsUrl() { + const url = new URL(this.apiUrl); + const wsProtocol = url.protocol === 'https:' ? 'wss' : 'ws'; + return `${wsProtocol}://${url.host + url.pathname}`; + } - get allTickersWSUrl() { return `wss://${this.apiUrl}/ws2/allTickers`; } + get candlesUrl() { + return `${this.apiUrl}/candles/candles`; + } - get tickerWSUrl() { return `wss://${this.apiUrl}/ws2/ticker/`; } + get allTickersWSUrl() { + return `${this.wsUrl}/ws2/allTickers`; + } - get lastPriceWSUrl() { return `wss://${this.apiUrl}/ws2/lastPrice/`; } + get tickerWSUrl() { + return `${this.wsUrl}/ws2/ticker/`; + } + + get lastPriceWSUrl() { + return `${this.wsUrl}/ws2/lastPrice/`; + } } export * as schemas from './schemas'; diff --git a/src/services/PriceFeed/ws/PriceFeedTickerWS.ts b/src/services/PriceFeed/ws/PriceFeedTickerWS.ts index 81628cb..8bae67f 100644 --- a/src/services/PriceFeed/ws/PriceFeedTickerWS.ts +++ b/src/services/PriceFeed/ws/PriceFeedTickerWS.ts @@ -10,6 +10,8 @@ const schema = z.tuple([ export default class PriceFeedTickerWS { priceWebSocket: WebSocket; + private heartbeatInterval: ReturnType; + constructor( symbol: string, url: string, @@ -17,7 +19,7 @@ export default class PriceFeedTickerWS { ) { this.priceWebSocket = new WebSocket(`${url}${symbol}`); - setInterval(() => { + this.heartbeatInterval = setInterval(() => { this.priceWebSocket.send('heartbeat'); }, 15000); @@ -32,6 +34,7 @@ export default class PriceFeedTickerWS { } kill() { + clearInterval(this.heartbeatInterval); this.priceWebSocket.close(); } }