diff --git a/README.md b/README.md index 3d2bf79..0f78145 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,9 @@ http://localhost:4000/api/v1/vaults/arbitrum/harvests/1717621855 https://clm-api.beefy.finance/api/v1/vaults/arbitrum/harvests/1712591753?vaults=0xeea4114ab4fcb82a28c514e21d656ca78d75b1a9&vaults=0x5b65c2d8866ee0cd0d041beb0c6ea53a1cd058cd&vaults=0x63b54b0e06028802007c5f1eaeac03d5472b904a&vaults=0x56637ef065dc19ed71b7bd8b60dbda9a1ba12a7e&vaults=0x9aa49971f4956d7831b2cd1c9af7ed931b5f91bc&vaults=0x4c32b8d26e6ab2ce401772514c999768f63afb4e&vaults=0xc670f18d0feef76ccb7c4c3ce0226cc64c8b6356&vaults=0x809f9007172beaae23c08352995e60b9f4c11bb2&vaults=0x8d8e012d80e2a7b3b4de2a050c0cf923a0064a8e&vaults=0xedd08a6ff7aeee1e4dccc103198af06b2316d8b8&vaults=0x2a2e016f9c30c7da5a41b21c19e9619ff78ab673&vaults=0xd3d8d178aaecde5ba307c8806cb04346bb91e307 http://localhost:4000/api/v1/vaults/arbitrum/harvests/1712591753?vaults=0xeea4114ab4fcb82a28c514e21d656ca78d75b1a9&vaults=0x5b65c2d8866ee0cd0d041beb0c6ea53a1cd058cd&vaults=0x63b54b0e06028802007c5f1eaeac03d5472b904a&vaults=0x56637ef065dc19ed71b7bd8b60dbda9a1ba12a7e&vaults=0x9aa49971f4956d7831b2cd1c9af7ed931b5f91bc&vaults=0x4c32b8d26e6ab2ce401772514c999768f63afb4e&vaults=0xc670f18d0feef76ccb7c4c3ce0226cc64c8b6356&vaults=0x809f9007172beaae23c08352995e60b9f4c11bb2&vaults=0x8d8e012d80e2a7b3b4de2a050c0cf923a0064a8e&vaults=0xedd08a6ff7aeee1e4dccc103198af06b2316d8b8&vaults=0x2a2e016f9c30c7da5a41b21c19e9619ff78ab673&vaults=0xd3d8d178aaecde5ba307c8806cb04346bb91e307 + + + +https://clm-api.beefy.finance/api/v1/status +http://localhost:4000/api/v1/status + diff --git a/package-lock.json b/package-lock.json index d6a5321..c3eb401 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@fastify/swagger": "^8.14.0", "@fastify/swagger-ui": "^3.0.0", "@fastify/under-pressure": "^8.3.0", + "@sinclair/typebox": "^0.32.33", "@types/lodash-es": "^4.17.12", "async-lock": "^1.4.1", "decimal.js": "^10.4.3", @@ -3682,6 +3683,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/schemas/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@jest/source-map": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", @@ -4456,10 +4463,9 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "version": "0.32.33", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.32.33.tgz", + "integrity": "sha512-jM50BfkKA0fwfj0uRRO6asfNfbU0oZipJIb/bL2+BUH/THjuEf2BMiqBOvKfBji5Z9t59NboZQGNfKZbdV50Iw==" }, "node_modules/@sindresorhus/is": { "version": "5.6.0", diff --git a/package.json b/package.json index 87e4cfd..cb1b542 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@fastify/swagger": "^8.14.0", "@fastify/swagger-ui": "^3.0.0", "@fastify/under-pressure": "^8.3.0", + "@sinclair/typebox": "^0.32.33", "@types/lodash-es": "^4.17.12", "async-lock": "^1.4.1", "decimal.js": "^10.4.3", diff --git a/src/config/chains.ts b/src/config/chains.ts index 5932cb6..ba75319 100644 --- a/src/config/chains.ts +++ b/src/config/chains.ts @@ -1,57 +1,20 @@ -import { keyBy } from 'lodash'; -import { keys } from '../utils/object'; +import { type Static, Type } from '@sinclair/typebox'; -export type Chain = { - id: T; - name: string; -}; +export const chainIdSchema = Type.Union([ + Type.Literal('arbitrum'), + Type.Literal('base'), + Type.Literal('optimism'), + Type.Literal('moonbeam'), + Type.Literal('linea'), + Type.Literal('polygon'), +]); +export type ChainId = Static; -function toChainMap>(arr: T) { - return keyBy(arr, 'id') as { [K in T[number]['id']]: Extract }; -} - -export const chains = toChainMap([ - { - id: 'arbitrum', - name: 'Arbitrum', - }, - { - id: 'base', - name: 'Base', - }, - { - id: 'optimism', - name: 'Optimism', - }, - { - id: 'moonbeam', - name: 'Moonbeam', - }, - { - id: 'linea', - name: 'Linea', - }, - { - id: 'polygon', - name: 'Polygon', - }, -] as const satisfies ReadonlyArray); - -export type Chains = typeof chains; -export type ChainId = keyof Chains; - -export const allChainIds = keys(chains); - -export function getChain(id: T): Chain { - if (id in chains) { - return chains[id]; - } - throw new Error(`Unknown chain: ${id}`); -} - -export function getChainOrUndefined(id: T): Chain | undefined { - if (id in chains) { - return chains[id]; - } - return undefined; -} +export const allChainIds: Array = [ + 'arbitrum', + 'base', + 'optimism', + 'moonbeam', + 'linea', + 'polygon', +]; diff --git a/src/routes/v1/status.ts b/src/routes/v1/status.ts index 3eb58dd..9ba332f 100644 --- a/src/routes/v1/status.ts +++ b/src/routes/v1/status.ts @@ -1,8 +1,8 @@ +import { type Static, Type } from '@sinclair/typebox'; import type { FastifyInstance, FastifyPluginOptions, FastifySchema } from 'fastify'; -import S from 'fluent-json-schema'; -import type { ChainId } from '../../config/chains'; +import { type ChainId, chainIdSchema } from '../../config/chains'; import { getAsyncCache } from '../../utils/async-lock'; -import { type SdkContext, getAllSdks } from '../../utils/sdk'; +import { getAllSdks, sdkContextSchema } from '../../utils/sdk'; export default async function ( instance: FastifyInstance, @@ -13,12 +13,10 @@ export default async function ( // status endpoint { - const responseSchema = S.object(); - const schema: FastifySchema = { tags: ['status'], response: { - 200: responseSchema, + 200: statusSchema, }, }; @@ -33,15 +31,17 @@ export default async function ( done(); } -type EndpointStatus = { - subgraph: SdkContext['subgraph']; - tag: SdkContext['tag']; - blockNumber: number | null; - timestamp: number | null; - hasErrors: boolean; -}; +const endpointStatusSchema = Type.Object({ + subgraph: sdkContextSchema.properties.subgraph, + tag: sdkContextSchema.properties.tag, + blockNumber: Type.Union([Type.Number(), Type.Null()]), + timestamp: Type.Union([Type.Number(), Type.Null()]), + hasErrors: Type.Boolean(), +}); +type EndpointStatus = Static; -type Status = Partial>; +const statusSchema = Type.Partial(Type.Record(chainIdSchema, Type.Array(endpointStatusSchema))); +type Status = Static; async function getStatus(): Promise { const sdks = getAllSdks(); diff --git a/src/utils/sdk.ts b/src/utils/sdk.ts index b2050d2..b140f21 100644 --- a/src/utils/sdk.ts +++ b/src/utils/sdk.ts @@ -1,5 +1,6 @@ +import { type Static, Type } from '@sinclair/typebox'; import { GraphQLClient } from 'graphql-request'; -import { type ChainId, allChainIds } from '../config/chains'; +import { type ChainId, allChainIds, chainIdSchema } from '../config/chains'; import { type Sdk, getSdk } from '../queries/codegen/sdk'; import { GraphQueryError } from './error'; import { createCachedFactoryByChainId } from './factory'; @@ -9,7 +10,13 @@ const SUBGRAPH_TAG = process.env.SUBGRAPH_TAG || 'latest'; const logger = getLoggerFor('sdk'); // adds the context to the response on all sdk queries -export type SdkContext = { chain: ChainId; subgraph: string; tag: string }; +export const sdkContextSchema = Type.Object({ + chain: chainIdSchema, + subgraph: Type.String(), + tag: Type.String(), +}); +export type SdkContext = Static; + type EndpointSdk = { [key in keyof Sdk]: ( ...args: Parameters