From 65c21de2ddcd87d38984a17cbb590f5d138aacc6 Mon Sep 17 00:00:00 2001 From: Alex Kraiz Date: Wed, 27 Sep 2023 16:08:09 +0400 Subject: [PATCH] feat!: added `Integrator` service --- src/Orion/index.ts | 3 + src/Unit/index.ts | 9 +- src/__tests__/basic.test.ts | 3 + src/config/schemas/pureEnvSchema.ts | 3 + src/services/Integrator/constants.ts | 1 + src/services/Integrator/index.ts | 121 ++++++++++++++++++ .../schemas/basic-pool-info-schema.ts | 26 ++++ .../schemas/environment-response-schema.ts | 19 +++ .../schemas/get-pool-response-schema.ts | 21 +++ src/services/Integrator/schemas/index.ts | 4 + .../Integrator/schemas/info-schema.ts | 12 ++ .../schemas/list-nft-order-response-schema.ts | 10 ++ .../schemas/list-pool-response-schema.ts | 26 ++++ .../Integrator/schemas/pool-schema.ts | 33 +++++ .../Integrator/schemas/util-schemas.ts | 14 ++ src/types.ts | 6 + 16 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 src/services/Integrator/constants.ts create mode 100644 src/services/Integrator/index.ts create mode 100644 src/services/Integrator/schemas/basic-pool-info-schema.ts create mode 100644 src/services/Integrator/schemas/environment-response-schema.ts create mode 100644 src/services/Integrator/schemas/get-pool-response-schema.ts create mode 100644 src/services/Integrator/schemas/index.ts create mode 100644 src/services/Integrator/schemas/info-schema.ts create mode 100644 src/services/Integrator/schemas/list-nft-order-response-schema.ts create mode 100644 src/services/Integrator/schemas/list-pool-response-schema.ts create mode 100644 src/services/Integrator/schemas/pool-schema.ts create mode 100644 src/services/Integrator/schemas/util-schemas.ts diff --git a/src/Orion/index.ts b/src/Orion/index.ts index 80dbbf1..b2a6b44 100644 --- a/src/Orion/index.ts +++ b/src/Orion/index.ts @@ -58,6 +58,9 @@ export default class Orion { priceFeed: { api: networkConfig.api + networkConfig.services.priceFeed.all, }, + integrator: { + api: networkConfig.api + networkConfig.services.integrator.http, + } }, }; }) diff --git a/src/Unit/index.ts b/src/Unit/index.ts index c8b4462..c9cc0f4 100644 --- a/src/Unit/index.ts +++ b/src/Unit/index.ts @@ -7,6 +7,7 @@ import Exchange from './Exchange/index.js'; import FarmingManager from './FarmingManager/index.js'; import { chains, envs } from '../config/index.js'; import type { networkCodes } from '../constants/index.js'; +import { IntegratorService } from '../services/Integrator/index.js'; type KnownConfig = { env: KnownEnv @@ -22,6 +23,8 @@ export default class Unit { public readonly blockchainService: BlockchainService; + public readonly integrator: IntegratorService; + public readonly aggregator: Aggregator; public readonly priceFeed: PriceFeed; @@ -58,6 +61,9 @@ export default class Unit { priceFeed: { api: networkConfig.api + networkConfig.services.priceFeed.all, }, + integrator: { + api: networkConfig.api + networkConfig.services.integrator.http, + } }, } } else { @@ -65,7 +71,7 @@ export default class Unit { } const chainInfo = chains[config.chainId]; if (!chainInfo) throw new Error('Chain info is required'); - + this.chainId = config.chainId; this.networkCode = chainInfo.code; this.contracts = chainInfo.contracts @@ -75,6 +81,7 @@ export default class Unit { this.provider.pollingInterval = 1000; this.blockchainService = new BlockchainService(this.config.services.blockchainService.http, this.config.basicAuth); + this.integrator = new IntegratorService(this.config.services.integrator.api, intNetwork); this.aggregator = new Aggregator( this.config.services.aggregator.http, this.config.services.aggregator.ws, diff --git a/src/__tests__/basic.test.ts b/src/__tests__/basic.test.ts index 6f8241b..c3c5c8a 100644 --- a/src/__tests__/basic.test.ts +++ b/src/__tests__/basic.test.ts @@ -177,6 +177,9 @@ describe('Orion', () => { priceFeed: { api: orionPriceFeedAPI + '/price-feed', }, + integrator: { + api: '', + } }, } } diff --git a/src/config/schemas/pureEnvSchema.ts b/src/config/schemas/pureEnvSchema.ts index 8838cd5..854e9f4 100644 --- a/src/config/schemas/pureEnvSchema.ts +++ b/src/config/schemas/pureEnvSchema.ts @@ -14,6 +14,9 @@ export const pureEnvNetworksSchema = z.object({ priceFeed: z.object({ all: z.string(), }), + integrator: z.object({ + http: z.string(), + }), }), rpc: z.string().optional(), liquidityMigratorAddress: z.string().optional(), diff --git a/src/services/Integrator/constants.ts b/src/services/Integrator/constants.ts new file mode 100644 index 0000000..00c0d3d --- /dev/null +++ b/src/services/Integrator/constants.ts @@ -0,0 +1 @@ +export const AVAILABLE_POOL_FEE = ['0.01', '0.05', '0.3', '1'] as const; diff --git a/src/services/Integrator/index.ts b/src/services/Integrator/index.ts new file mode 100644 index 0000000..ebb36ce --- /dev/null +++ b/src/services/Integrator/index.ts @@ -0,0 +1,121 @@ +import { + environmentResponseSchema, + getPoolResponseSchema, + listNFTOrderResponseSchema, + listPoolResponseSchema, +} from './schemas/index.js'; +import { fetchWithValidation } from 'simple-typed-fetch'; + +type BasePayload = { + chainId: number + jsonrpc: '1.0' +}; + +type GetEnvironmentPayload = BasePayload & { + model: 'Environment' + method: 'getEnvironment' + params: [] +}; + +type ListNFTOrderPayload = BasePayload & { + model: 'OrionV3NFTManager' + method: 'listNFTOrder' + params: [string] +}; + +type GetPoolInfoPayload = BasePayload & { + model: 'OrionV3Factory' + method: 'getPoolInfo' + params: [string, string, string] +}; + +type ListPoolPayload = BasePayload & { + model: 'OrionFarmV3' + method: 'listPool' + params: [string] +}; + +type Payload = + | GetEnvironmentPayload + | ListNFTOrderPayload + | GetPoolInfoPayload + | ListPoolPayload; + +class IntegratorService { + private readonly apiUrl: string; + + private readonly chainId: number; + + get api() { + return this.apiUrl; + } + + constructor(apiUrl: string, chainId: number) { + this.apiUrl = apiUrl; + this.chainId = chainId; + + this.getEnvironment = this.getEnvironment.bind(this); + this.listNFTOrder = this.listNFTOrder.bind(this); + this.getPoolInfo = this.getPoolInfo.bind(this); + this.listPool = this.listPool.bind(this); + } + + makeRPCPayload = (payload: Omit) => { + return JSON.stringify({ + ...payload, + chainId: this.chainId, + jsonrpc: '1.0', + }); + }; + + private readonly getEnvironment = () => { + return fetchWithValidation(this.apiUrl, environmentResponseSchema, { + method: 'POST', + body: this.makeRPCPayload({ + model: 'Environment', + method: 'getEnvironment', + params: [], + }), + }); + }; + + private readonly listNFTOrder = (address: string) => { + return fetchWithValidation(this.apiUrl, listNFTOrderResponseSchema, { + method: 'POST', + body: this.makeRPCPayload({ + model: 'OrionV3NFTManager', + method: 'listNFTOrder', + params: [address], + }), + }); + }; + + private readonly getPoolInfo = ( + token0: string, + token1: string, + poolAddress: string + ) => { + return fetchWithValidation(this.apiUrl, getPoolResponseSchema, { + method: 'POST', + body: this.makeRPCPayload({ + model: 'OrionV3Factory', + method: 'getPoolInfo', + params: [token0, token1, poolAddress], + }), + }); + } + + private readonly listPool = (address: string) => { + return fetchWithValidation(this.apiUrl, listPoolResponseSchema, { + method: 'POST', + body: this.makeRPCPayload({ + model: 'OrionFarmV3', + method: 'listPool', + params: [address], + }), + }); + } +} + +export * as schemas from './schemas/index.js'; +export { IntegratorService }; diff --git a/src/services/Integrator/schemas/basic-pool-info-schema.ts b/src/services/Integrator/schemas/basic-pool-info-schema.ts new file mode 100644 index 0000000..049c92f --- /dev/null +++ b/src/services/Integrator/schemas/basic-pool-info-schema.ts @@ -0,0 +1,26 @@ +import { z } from 'zod'; +import { evmAddressSchema } from './util-schemas.js'; + +const basicPoolInfo = z.object({ + poolAddress: evmAddressSchema, + isInitialized: z.boolean(), + liquidity: z.number().nonnegative(), + liquidityInUsd: z.number().nonnegative(), + liquidityShare: z.number().nonnegative(), + isFarming: z.boolean(), + rewardsTotal: z.number().nonnegative(), + rewardsPerPeriod: z.number().nonnegative(), + rewardsShare: z.number().nonnegative(), + feePerPeriod: z.number().nonnegative(), + feeTotal: z.number().nonnegative(), + feeShare: z.number().nonnegative(), + tickMultiplier: z.number().nonnegative(), + MAX_TICK: z.number().nonnegative().int(), + minAPR: z.number().nonnegative(), + maxAPR: z.number().nonnegative(), + avgAPR: z.number().nonnegative(), + maxBoost: z.number().nonnegative().int(), + feeRate: z.array(z.number().nonnegative()), +}); + +export default basicPoolInfo; diff --git a/src/services/Integrator/schemas/environment-response-schema.ts b/src/services/Integrator/schemas/environment-response-schema.ts new file mode 100644 index 0000000..5da5cd2 --- /dev/null +++ b/src/services/Integrator/schemas/environment-response-schema.ts @@ -0,0 +1,19 @@ +import { z } from 'zod'; +import { evmAddressSchema } from './util-schemas.js'; +import infoSchema from './info-schema.js'; + +const environmentResponseSchema = z.object({ + result: z.object({ + chainId: z.number().int().nonnegative(), + nativeToken: z.string(), + OrionV3Factory: evmAddressSchema, + OrionV3NFTManager: evmAddressSchema, + SwapRouter: evmAddressSchema, + OrionFarmV3: evmAddressSchema, + OrionVoting: evmAddressSchema, + veORN: evmAddressSchema, + }), + info: infoSchema, +}); + +export default environmentResponseSchema; diff --git a/src/services/Integrator/schemas/get-pool-response-schema.ts b/src/services/Integrator/schemas/get-pool-response-schema.ts new file mode 100644 index 0000000..ee49682 --- /dev/null +++ b/src/services/Integrator/schemas/get-pool-response-schema.ts @@ -0,0 +1,21 @@ +import { z } from 'zod'; +import { evmAddressSchema } from './util-schemas.js'; +import { AVAILABLE_POOL_FEE } from '../constants.js'; +import basicPoolInfo from './basic-pool-info-schema.js'; +import infoSchema from './info-schema.js'; + +const getPoolResponseSchema = z.object({ + result: z.object({ + token0: z.string().nonempty(), + token1: z.string().nonempty(), + token0Address: evmAddressSchema, + token1Address: evmAddressSchema, + + totalLiquidity: z.number().nonnegative(), + WETH9: evmAddressSchema, + pools: z.record(z.enum(AVAILABLE_POOL_FEE), basicPoolInfo.nullable()), + }), + info: infoSchema, +}); + +export default getPoolResponseSchema; diff --git a/src/services/Integrator/schemas/index.ts b/src/services/Integrator/schemas/index.ts new file mode 100644 index 0000000..1d09d6e --- /dev/null +++ b/src/services/Integrator/schemas/index.ts @@ -0,0 +1,4 @@ +export { default as environmentResponseSchema } from './environment-response-schema.js'; +export { default as listNFTOrderResponseSchema } from './list-nft-order-response-schema.js'; +export { default as getPoolResponseSchema } from './get-pool-response-schema.js'; +export { default as listPoolResponseSchema } from './list-pool-response-schema.js'; diff --git a/src/services/Integrator/schemas/info-schema.ts b/src/services/Integrator/schemas/info-schema.ts new file mode 100644 index 0000000..949727e --- /dev/null +++ b/src/services/Integrator/schemas/info-schema.ts @@ -0,0 +1,12 @@ +import { z } from 'zod'; +import { hexStringSchema } from './util-schemas.js'; + +const infoSchema = z.object({ + blockNumber: z.number().int().nonnegative(), + blockHash: hexStringSchema, + timeRequest: z.number().int().nonnegative(), + timeAnswer: z.number().int().nonnegative(), + changes: z.number().int().nonnegative(), +}); + +export default infoSchema; diff --git a/src/services/Integrator/schemas/list-nft-order-response-schema.ts b/src/services/Integrator/schemas/list-nft-order-response-schema.ts new file mode 100644 index 0000000..4747ffd --- /dev/null +++ b/src/services/Integrator/schemas/list-nft-order-response-schema.ts @@ -0,0 +1,10 @@ +import { z } from 'zod'; +import poolSchema from './pool-schema.js'; +import infoSchema from './info-schema.js'; + +const listNFTOrderResponseSchema = z.object({ + result: z.array(poolSchema), + info: infoSchema, +}); + +export default listNFTOrderResponseSchema; diff --git a/src/services/Integrator/schemas/list-pool-response-schema.ts b/src/services/Integrator/schemas/list-pool-response-schema.ts new file mode 100644 index 0000000..2e19738 --- /dev/null +++ b/src/services/Integrator/schemas/list-pool-response-schema.ts @@ -0,0 +1,26 @@ +import { z } from 'zod'; +import { evmAddressSchema } from './util-schemas.js'; +import basicPoolInfo from './basic-pool-info-schema.js'; +import infoSchema from './info-schema.js'; + +const poolOfListPoolSchema = z.object({ + token0: z.string().nonempty(), + token1: z.string().nonempty(), + token0Address: evmAddressSchema, + token1Address: evmAddressSchema, + + token0Decimals: z.number().int().nonnegative().max(18), + token1Decimals: z.number().int().nonnegative().max(18), + WETH9: evmAddressSchema, + + ...basicPoolInfo.shape, + + type: z.string().nonempty(), +}); + +const listPoolResponseSchema = z.object({ + result: z.array(poolOfListPoolSchema), + info: infoSchema, +}); + +export default listPoolResponseSchema; diff --git a/src/services/Integrator/schemas/pool-schema.ts b/src/services/Integrator/schemas/pool-schema.ts new file mode 100644 index 0000000..3053734 --- /dev/null +++ b/src/services/Integrator/schemas/pool-schema.ts @@ -0,0 +1,33 @@ +import { z } from 'zod'; +import { evmAddressSchema } from './util-schemas.js'; + +const poolSchema = z.object({ + tokenId: evmAddressSchema, + + token0: z.string().nonempty(), + token1: z.string().nonempty(), + token0Address: evmAddressSchema, + token1Address: evmAddressSchema, + token0Decimals: z.number().int().nonnegative().max(18), + token1Decimals: z.number().int().nonnegative().max(18), + + amount: z.number().nonnegative(), + amount0: z.number().nonnegative(), + amount1: z.number().nonnegative(), + from: z.number().nonnegative(), + to: z.number().nonnegative(), + fee: z.number().nonnegative(), + collectFee: z.number().nonnegative(), + reward: z.number().nonnegative(), + apr: z.number().nonnegative(), + boost: z.number().int().nonnegative(), + isStaked: z.boolean(), + poolFee: z.number().nonnegative(), + poolAddress: evmAddressSchema, + veOrnForMaxBoost: z.number().nonnegative(), + veOrnMaxBoost: z.number().nonnegative(), + veORNCurrent: z.number().nonnegative(), + time: z.number().int().nonnegative(), // tim +}); + +export default poolSchema; diff --git a/src/services/Integrator/schemas/util-schemas.ts b/src/services/Integrator/schemas/util-schemas.ts new file mode 100644 index 0000000..0d58a75 --- /dev/null +++ b/src/services/Integrator/schemas/util-schemas.ts @@ -0,0 +1,14 @@ +import { ethers } from 'ethers'; +import { z } from 'zod'; + +export const evmAddressSchema = z + .string() + .refine(ethers.utils.isAddress, (v) => ({ + message: `${v} is not a valid address`, + })); + +export const hexStringSchema = z + .string() + .refine(ethers.utils.isAddress, (v) => ({ + message: `${v} is not a valid hex string`, + })); diff --git a/src/types.ts b/src/types.ts index 64449e8..88d32ac 100644 --- a/src/types.ts +++ b/src/types.ts @@ -252,6 +252,12 @@ export type VerboseUnitConfig = { // http://10.23.5.11:3003/, // https://price-feed:3003/ } + integrator: { + api: string + // For example: + // http://localhost:3004/, + // http:// + } } basicAuth?: BasicAuthCredentials }