diff --git a/package.json b/package.json index 6161bf5e..b157f60e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orionprotocol/sdk", - "version": "0.20.86", + "version": "0.20.79-rc23", "description": "Orion Protocol SDK", "main": "./lib/index.cjs", "module": "./lib/index.js", diff --git a/src/Orion/index.ts b/src/Orion/index.ts index 8ddf534a..6230d227 100644 --- a/src/Orion/index.ts +++ b/src/Orion/index.ts @@ -1,12 +1,13 @@ import { merge } from 'merge-anything'; -import { chains, envs } from '../config/index.js'; +import { chains, envs } from '../config'; import type { networkCodes } from '../constants/index.js'; import Unit from '../Unit/index.js'; -import { ReferralSystem } from '../services/ReferralSystem/index.js'; +import { ReferralSystem } from '../services/ReferralSystem'; import type { SupportedChainId, DeepPartial, VerboseUnitConfig, KnownEnv, EnvConfig, AggregatedAssets } from '../types.js'; import { isValidChainId } from '../utils/index.js'; import { simpleFetch } from 'simple-typed-fetch'; import Bridge from './bridge/index.js'; +import { Frontage } from '../services/Frontage'; export default class Orion { public readonly env?: string; @@ -17,6 +18,8 @@ export default class Orion { public readonly bridge: Bridge; + public readonly frontage: Frontage; + // TODO: get tradable assets (aggregated) // TODO: get tradable pairs (aggregated) @@ -40,6 +43,7 @@ export default class Orion { config = { analyticsAPI: envConfig?.analyticsAPI, referralAPI: envConfig.referralAPI, + frontageAPI: envConfig.frontageAPI, networks: Object.entries(envConfig.networks).map(([chainId, networkConfig]) => { if (!isValidChainId(chainId)) throw new Error(`Invalid chainId: ${chainId}`); const chainConfig = chains[chainId]; @@ -107,6 +111,8 @@ export default class Orion { this.bridge = new Bridge( this.unitsArray, ); + + this.frontage = new Frontage(config.frontageAPI); } get unitsArray() { diff --git a/src/Unit/index.ts b/src/Unit/index.ts index 9b504cc5..7bf23745 100644 --- a/src/Unit/index.ts +++ b/src/Unit/index.ts @@ -11,7 +11,7 @@ import Exchange from './Exchange/index.js'; import { chains, envs } from '../config'; import type { networkCodes } from '../constants/index.js'; import { IndexerService } from '../services/Indexer'; -import Pmm from "./Pmm"; +import Pmm from './Pmm'; type KnownConfig = { env: KnownEnv @@ -90,7 +90,7 @@ export default class Unit { }, indexer: { api: networkConfig.api + networkConfig.services.indexer?.http, - }, + } }, }; } else { diff --git a/src/config/envs.json b/src/config/envs.json index 12e87f9b..c5870d77 100644 --- a/src/config/envs.json +++ b/src/config/envs.json @@ -1,6 +1,7 @@ { "production": { "referralAPI": "https://trade.orion.xyz/referral-api", + "frontageAPI": "https://trade.orion.xyz/frontage", "networks": { "1": { "api": "https://trade.orion.xyz/eth-mainnet", @@ -205,6 +206,7 @@ }, "testing": { "referralAPI": "https://testing.orion.xyz/referral-api", + "frontageAPI": "https://testing.orion.xyz/frontage", "networks": { "97": { "api": "https://testing.orion.xyz/bsc-testnet", @@ -319,6 +321,7 @@ }, "staging": { "referralAPI": "https://staging.orion.xyz/referral-api", + "frontageAPI": "https://staging.orion.xyz/frontage", "networks": { "1": { "api": "https://staging.orion.xyz/eth-mainnet", @@ -522,6 +525,7 @@ }, "experimental": { "referralAPI": "https://testing.orion.xyz/referral-api", + "frontageAPI": "https://testing.orion.xyz/frontage", "networks": { "97": { "api": "https://dn-dev.orion.xyz/bsc-testnet", @@ -563,6 +567,7 @@ }, "kucoin-production": { "referralAPI": "https://trade.orion.xyz/referral-api", + "frontageAPI": "https://trade.orion.xyz/frontage", "networks": { "1": { "api": "https://trade.orion.xyz/eth-mainnet", diff --git a/src/config/schemas/pureEnvSchema.ts b/src/config/schemas/pureEnvSchema.ts index 9c799c44..8f414be0 100644 --- a/src/config/schemas/pureEnvSchema.ts +++ b/src/config/schemas/pureEnvSchema.ts @@ -16,7 +16,7 @@ export const pureEnvNetworksSchema = z.object({ }), indexer: z.object({ http: z.string(), - }).optional(), + }).optional() }), rpc: z.string().optional(), liquidityMigratorAddress: z.string().optional(), @@ -25,6 +25,7 @@ export const pureEnvNetworksSchema = z.object({ export const pureEnvPayloadSchema = z.object({ analyticsAPI: z.string().url().optional(), referralAPI: z.string().url(), + frontageAPI: z.string().url(), networks: z.record( z.nativeEnum(SupportedChainId), pureEnvNetworksSchema diff --git a/src/services/Frontage/index.ts b/src/services/Frontage/index.ts new file mode 100644 index 00000000..1d597c83 --- /dev/null +++ b/src/services/Frontage/index.ts @@ -0,0 +1,78 @@ +import { fetchWithValidation } from 'simple-typed-fetch'; +import { tickersSchema } from './schemas'; +import type { TickersBaseSearchParams, TickersCategories } from '../../types'; + +export class Frontage { + private readonly apiUrl: string; + + constructor(apiUrl: string) { + this.apiUrl = apiUrl; + } + + searchTickers = ({ + searchValue, + currentNetwork, + targetNetwork, + sortBy, + sortType, + offset, + limit, + }: { searchValue: string } & TickersBaseSearchParams) => { + const url = new URL(this.apiUrl); + const params = new URLSearchParams(); + + params.set('searchValue', encodeURIComponent(searchValue)); + if (currentNetwork !== undefined) params.set('currentNetwork', encodeURIComponent(currentNetwork).toUpperCase()); + if (targetNetwork !== undefined) params.set('targetNetwork', encodeURIComponent(targetNetwork).toUpperCase()); + if (sortBy !== undefined) params.set('sortBy', encodeURIComponent(sortBy)); + if (sortType !== undefined) params.set('sortType', encodeURIComponent(sortType)); + if (offset !== undefined) params.set('offset', offset.toString()); + if (limit !== undefined) params.set('limit', limit.toString()); + + url.pathname += '/api/v1/tickers/search'; + url.search = params.toString(); + + return fetchWithValidation( + url.toString(), + tickersSchema + ); + }; + + getTickers = ({ + category, + currentNetwork, + targetNetwork, + sortBy, + sortType, + offset, + limit, + tickers, + }: { category: TickersCategories, tickers?: string } & TickersBaseSearchParams) => { + const url = new URL(this.apiUrl); + const params = new URLSearchParams(); + + if (category === 'FAVORITES' && tickers !== undefined) params.set('tickers', tickers); + if (category !== 'FAVORITES') params.set('category', category); + if (currentNetwork !== undefined) params.set('currentNetwork', encodeURIComponent(currentNetwork).toUpperCase()); + if (targetNetwork !== undefined) params.set('targetNetwork', encodeURIComponent(targetNetwork).toUpperCase()); + if (sortBy !== undefined) params.set('sortBy', encodeURIComponent(sortBy)); + if (sortType !== undefined) params.set('sortType', encodeURIComponent(sortType)); + if (offset !== undefined) params.set('offset', offset.toString()); + if (limit !== undefined) params.set('limit', limit.toString()); + + if (category === 'FAVORITES' && tickers !== undefined) { + url.pathname += '/api/v1/tickers/get/favourites'; + } else { + url.pathname += '/api/v1/tickers/get/category'; + } + + url.search = params.toString(); + + return fetchWithValidation( + url.toString(), + tickersSchema + ); + }; +} + +export * as schemas from './schemas/index.js'; diff --git a/src/services/Frontage/schemas/index.ts b/src/services/Frontage/schemas/index.ts new file mode 100644 index 00000000..4611949c --- /dev/null +++ b/src/services/Frontage/schemas/index.ts @@ -0,0 +1 @@ +export * from './tickers-schema'; diff --git a/src/services/Frontage/schemas/tickers-schema.ts b/src/services/Frontage/schemas/tickers-schema.ts new file mode 100644 index 00000000..a50b7423 --- /dev/null +++ b/src/services/Frontage/schemas/tickers-schema.ts @@ -0,0 +1,13 @@ +import { z } from 'zod'; +import { SupportedChainId } from '../../../types'; + +export const tickerSchema = z.object({ + pair: z.string(), + volume24: z.number(), + change24: z.number(), + lastPrice: z.number(), + pricePrecision: z.number(), + networks: z.array(z.nativeEnum(SupportedChainId)), +}); + +export const tickersSchema = z.array(tickerSchema); diff --git a/src/services/index.ts b/src/services/index.ts index ca04ebd0..004c45f6 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -3,3 +3,4 @@ export * as blockchainService from './BlockchainService/index.js'; export * as priceFeed from './PriceFeed/index.js'; export * as referralSystem from './ReferralSystem/index.js'; export * as indexer from './Indexer/index.js'; +export * as frontage from './Frontage/index.js'; diff --git a/src/types.ts b/src/types.ts index 6a3513ac..9450633c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,7 @@ import type subOrderStatuses from './constants/subOrderStatuses.js'; import type positionStatuses from './constants/positionStatuses.js'; import type { knownEnvs } from './config/schemas'; import type getHistory from './Orion/bridge/getHistory.js'; +import type { networkCodes } from './constants'; export type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; @@ -284,6 +285,7 @@ export type Json = string | number | boolean | null | Json[] | { [key: string]: export type EnvConfig = { analyticsAPI: string | undefined referralAPI: string + frontageAPI: string networks: Partial< Record< SupportedChainId, @@ -461,3 +463,21 @@ export type AtomicSwap = Partial< } export type OrderSource = 'TERMINAL_MARKET' | 'TERMINAL_LIMIT' | 'SWAP_UI' | 'WIDGET'; + +// Frontage +export type NetworkCode = typeof networkCodes[number]; + +export type TickersCategories = 'FAVORITES' | 'USD' | 'ORN' | 'NATIVE' | 'ALTS'; + +export type TickersSortBy = 'PRICE' | 'CHANGE' | 'VOLUME'; + +export type TickersSortType = 'ASCENDING' | 'DESCENDING'; + +export type TickersBaseSearchParams = { + currentNetwork?: NetworkCode + targetNetwork?: NetworkCode + sortBy?: TickersSortBy + sortType?: TickersSortType + offset?: number + limit?: number +}