diff --git a/docs/all.yaml b/docs/all.yaml index 565a443..ce72e84 100644 --- a/docs/all.yaml +++ b/docs/all.yaml @@ -1324,6 +1324,7 @@ paths: description: Type of the tractor order enum: - SOW_V0 + - CONVERT_UP_V0 betweenTimes: type: array description: Date range for snapshot time filtering @@ -1371,6 +1372,9 @@ paths: snapshotBlock: type: integer description: Block number when the snapshot was taken + season: + type: integer + description: Season number of this snapshot totalPintoSown: type: string description: (SOW_V0) Cumulative amount of Pinto sown at snapshot time @@ -1383,18 +1387,41 @@ paths: totalCascadeFundedAnyTemp: type: string description: (SOW_V0) Total funded amount that can be executed at any temperature + totalBeansConverted: + type: string + description: (CONVERT_UP_V0) Cumulative amount of Beans converted at snapshot time + totalGsBonusStalk: + type: string + description: (CONVERT_UP_V0) Total Grown Stalk bonus received from conversions + totalGsBonusBdv: + type: string + description: (CONVERT_UP_V0) Total BDV associated with Grown Stalk bonuses + totalGsPenaltyStalk: + type: string + description: (CONVERT_UP_V0) Total Grown Stalk penalty from conversions + totalGsPenaltyBdv: + type: string + description: (CONVERT_UP_V0) Total BDV associated with Grown Stalk penalties + totalCascadeFunded: + type: string + description: (CONVERT_UP_V0) Total amount funded in cascade + totalCascadeFundedExecutable: + type: string + description: (CONVERT_UP_V0) Total funded amount that is currently executable totalTipsPaid: type: string - description: (SOW_V0) Cumulative tips paid at snapshot time + description: Cumulative tips paid to this order type at snapshot time currentMaxTip: type: string - description: (SOW_V0) Current maximum tip at snapshot time + description: Current maximum tip of this order type at snapshot time totalExecutions: type: integer - description: (SOW_V0) Cumulative number of executions at snapshot time + description: Cumulative number of executions of this order type at snapshot time + uniquePublishers: + type: integer + description: Number of unique publishers of this order type at snapshot time example: lastUpdated: 29374836 - totalRecords: 25 snapshots: - id: 172 snapshotTimestamp: "2025-04-23T23:00:01.000Z" @@ -1406,6 +1433,7 @@ paths: totalTipsPaid: "26700000" currentMaxTip: "910000" totalExecutions: 71 + totalRecords: 25 '400': description: Bad Request content: @@ -1416,6 +1444,167 @@ paths: error: type: string example: "Invalid date range provided." + /tractor/v2/snapshots: + post: + tags: + - Tractor + summary: Get tractor state snapshots (v2) + description: Returns historical snapshots of tractor metrics for multiple order types. This endpoint allows querying multiple order types at once and returns them organized by order type. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + orderTypes: + type: array + description: Array of tractor order types to query. If omitted, all order types are returned. + items: + type: string + enum: + - SOW_V0 + - CONVERT_UP_V0 + betweenTimes: + type: array + description: Date range for snapshot time filtering + items: + type: string + format: date-time + minItems: 2 + maxItems: 2 + betweenSeasons: + type: array + description: Season range for snapshot time filtering + items: + type: integer + minItems: 2 + maxItems: 2 + limit: + type: number + description: Maximum number of snapshots to return per order type + skip: + type: number + description: Number of snapshots to skip (for pagination) + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + lastUpdated: + type: integer + description: Block number of the last update + maxRecords: + type: integer + description: Maximum number of total records across all queried order types + snapshots: + type: object + description: Snapshots organized by order type + additionalProperties: + type: array + items: + type: object + properties: + snapshotTimestamp: + type: string + format: date-time + description: When the snapshot was taken + snapshotBlock: + type: integer + description: Block number when the snapshot was taken + season: + type: integer + description: Season number of this snapshot + totalPintoSown: + type: string + description: (SOW_V0) Cumulative amount of Pinto sown at snapshot time + totalPodsMinted: + type: string + description: (SOW_V0) Cumulative number of pods minted at snapshot time + totalCascadeFundedBelowTemp: + type: string + description: (SOW_V0) Total funded amount that can be executed at the current temperature + totalCascadeFundedAnyTemp: + type: string + description: (SOW_V0) Total funded amount that can be executed at any temperature + totalBeansConverted: + type: string + description: (CONVERT_UP_V0) Cumulative amount of Beans converted at snapshot time + totalGsBonusStalk: + type: string + description: (CONVERT_UP_V0) Total Grown Stalk bonus received from conversions + totalGsBonusBdv: + type: string + description: (CONVERT_UP_V0) Total BDV associated with Grown Stalk bonuses + totalGsPenaltyStalk: + type: string + description: (CONVERT_UP_V0) Total Grown Stalk penalty from conversions + totalGsPenaltyBdv: + type: string + description: (CONVERT_UP_V0) Total BDV associated with Grown Stalk penalties + totalCascadeFunded: + type: string + description: (CONVERT_UP_V0) Total amount funded in cascade + totalCascadeFundedExecutable: + type: string + description: (CONVERT_UP_V0) Total funded amount that is currently executable + totalTipsPaid: + type: string + description: Cumulative tips paid to this order type at snapshot time + currentMaxTip: + type: string + description: Current maximum tip of this order type at snapshot time + totalExecutions: + type: integer + description: Cumulative number of executions of this order type at snapshot time + uniquePublishers: + type: integer + description: Number of unique publishers of this order type at snapshot time + example: + lastUpdated: 29374836 + snapshots: + SOW_V0: + - id: 172 + snapshotTimestamp: "2025-04-23T23:00:01.000Z" + snapshotBlock: 29329927 + season: 3700 + totalPintoSown: "25114326676" + totalPodsMinted: "368220397685" + totalCascadeFundedBelowTemp: "24198821026" + totalCascadeFundedAnyTemp: "69015496855" + totalTipsPaid: "26700000" + currentMaxTip: "910000" + totalExecutions: 71 + uniquePublishers: 4 + CONVERT_UP_V0: + - snapshotTimestamp: "2025-11-06T09:00:01.000Z" + snapshotBlock: 37815127 + season: 8426 + totalBeansConverted: "12000000" + totalGsBonusStalk: "184994244374079" + totalGsBonusBdv: "90266" + totalGsPenaltyStalk: "0" + totalGsPenaltyBdv: "0" + totalCascadeFunded: "4153289614" + totalCascadeFundedExecutable: "0" + totalTipsPaid: "300000" + currentMaxTip: "0" + totalExecutions: 2 + uniquePublishers: 4 + maxRecords: 25 + '400': + description: Bad Request + content: + application/json: + schema: + type: object + properties: + error: + type: string + example: "Invalid orderType provided." /seasons: get: tags: diff --git a/src/routes/tractor-routes.js b/src/routes/tractor-routes.js index 3758504..c626e31 100644 --- a/src/routes/tractor-routes.js +++ b/src/routes/tractor-routes.js @@ -96,6 +96,7 @@ router.post('/executions', async (ctx) => { /** * Returns all tractor state snapshots matching the requested criteria. + * @deprecated use v2 instead */ router.post('/snapshots', async (ctx) => { /** @type {import('../../types/types').TractorSnapshotsRequest} */ @@ -129,4 +130,50 @@ router.post('/snapshots', async (ctx) => { ctx.body = results; }); +/** + * Returns all tractor state snapshots matching the requested criteria. + */ +router.post('/v2/snapshots', async (ctx) => { + /** @type {import('../../types/types').TractorV2SnapshotsRequest} */ + const body = ctx.request.body; + + if ( + (body.orderTypes && !Array.isArray(body.orderTypes)) || + (body.limit !== undefined && typeof body.limit !== 'number') || + (body.skip !== undefined && typeof body.skip !== 'number') + ) { + throw new InputError('Invalid type provided for body parameter.'); + } + + const orderTypes = body.orderTypes ?? Object.keys(TractorOrderType); + if (orderTypes.some((type) => !TractorOrderType[type])) { + throw new InputError('Invalid orderType provided.'); + } + + body.between = body.between?.map((v) => new Date(v)); + RestParsingUtil.dateRangeValidation(body.between); + + RestParsingUtil.numberRangeValidation(body.betweenSeasons); + + const results = await Promise.all( + orderTypes.map(async (type) => { + if (type === 'SOW_V0') { + return await SnapshotSowV0Service.getSnapshots(body); + } else if (type === 'CONVERT_UP_V0') { + return await SnapshotConvertUpV0Service.getSnapshots(body); + } + }) + ); + + /** @type {import('../../types/types').TractorV2SnapshotsResult} */ + ctx.body = { + lastUpdated: results[0].lastUpdated, + snapshots: results.reduce((acc, next, idx) => { + acc[orderTypes[idx]] = next.snapshots; + return acc; + }, {}), + maxRecords: results.reduce((max, result) => Math.max(max, result.totalRecords), 0) + }; +}); + module.exports = router; diff --git a/types/types.d.ts b/types/types.d.ts index d3b2758..2676c3a 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -5,7 +5,10 @@ import SowV0OrderDto from '../src/repository/dto/tractor/SowV0OrderDto'; import SowV0ExecutionDto from '../src/repository/dto/tractor/SowV0ExecutionDto'; import { TractorOrderType } from '../src/repository/postgres/models/types/types'; import SnapshotSowV0Dto from '../src/repository/dto/tractor/SnapshotSowV0Dto'; +import SnapshotConvertUpV0Dto from '../src/repository/dto/tractor/SnapshotConvertUpV0Dto'; import CombinedInflowSnapshotDto from '../src/repository/dto/inflow/CombinedInflowSnapshotDto'; +import ConvertUpV0ExecutionDto from '../src/repository/dto/tractor/ConvertUpV0ExecutionDto'; +import ConvertUpV0OrderDto from '../src/repository/dto/tractor/ConvertUpV0OrderDto'; export type DepositYield = { // Percentage growth in deposit's bdv @@ -121,11 +124,14 @@ type SowV0ExecutionRequestParams = { usedToken?: string; }; +type ConvertUpV0OrderRequestParams = SowV0OrderRequestParams; +type ConvertUpV0ExecutionRequestParams = SowV0ExecutionRequestParams; + // Union types for all possible blueprint-specific values -type BlueprintOrderRequestParams = SowV0OrderRequestParams; -type BlueprintOrderResponse = SowV0OrderDto; -type BlueprintExecutionRequestParams = SowV0ExecutionRequestParams; -type BlueprintExecutionResponse = SowV0ExecutionDto; +type BlueprintOrderRequestParams = SowV0OrderRequestParams | ConvertUpV0OrderRequestParams; +type BlueprintOrderResponse = SowV0OrderDto | ConvertUpV0OrderDto; +type BlueprintExecutionRequestParams = SowV0ExecutionRequestParams | ConvertUpV0ExecutionRequestParams; +type BlueprintExecutionResponse = SowV0ExecutionDto | ConvertUpV0ExecutionDto; export type TractorOrderRequest = { orderType?: keyof TractorOrderType | 'KNOWN' | 'UKNOWN'; @@ -178,7 +184,7 @@ export type TractorExecutionsResult = { }; export type TractorSnapshotsRequest = { - orderType?: keyof TractorOrderType | 'KNOWN' | 'UKNOWN'; + orderType: keyof TractorOrderType; betweenTimes?: [Date, Date]; betweenSeasons?: [number, number]; limit?: number; @@ -186,7 +192,7 @@ export type TractorSnapshotsRequest = { }; // Potential union type as more are added -type SnapshotResponse = SnapshotSowV0Dto; +type SnapshotResponse = SnapshotSowV0Dto | SnapshotConvertUpV0Dto; export type TractorSnapshotsResult = { // Block number lastUpdated: number; @@ -206,3 +212,18 @@ export type CombinedInflowSnapshotsResult = { snapshots: CombinedInflowSnapshotResponse[]; totalRecords: number; }; + +export type TractorV2SnapshotsRequest = { + orderTypes?: Array; + betweenTimes?: [Date, Date]; + betweenSeasons?: [number, number]; + limit?: number; + skip?: number; +}; + +export type TractorV2SnapshotsResult = { + // Block number + lastUpdated: number; + snapshots: Record; + maxRecords: number; +};