From 030c9f889dba2ca66053017086bdba5ac1460110 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 20 Oct 2025 18:32:04 -0700 Subject: [PATCH 1/4] add athena query for recent fill premium/discounts --- src/athena/client.ts | 215 ++++++++++++++ src/athena/index.ts | 4 + .../repositories/fillQualityAnalytics.ts | 280 ++++++++++++++++++ src/athena/utils.ts | 40 +++ 4 files changed, 539 insertions(+) create mode 100644 src/athena/client.ts create mode 100644 src/athena/index.ts create mode 100644 src/athena/repositories/fillQualityAnalytics.ts create mode 100644 src/athena/utils.ts diff --git a/src/athena/client.ts b/src/athena/client.ts new file mode 100644 index 0000000..c0494f7 --- /dev/null +++ b/src/athena/client.ts @@ -0,0 +1,215 @@ +import { + AthenaClient, + ColumnInfo, + GetQueryExecutionCommand, + GetQueryResultsCommand, + GetQueryResultsCommandOutput, + QueryExecutionState, + Row, + StartQueryExecutionCommand, + StartQueryExecutionCommandInput, +} from '@aws-sdk/client-athena'; +import { ConfiguredRetryStrategy } from '@aws-sdk/util-retry'; +import Bottleneck from 'bottleneck'; +import { logger } from '../utils/logger'; + +const QUERY_TIMEOUT = 300000; +const POLL_INTERVAL = 1000; + +const DEFAULT_ATHENA_DATABASE = 'staging-archive'; +const DEFAULT_ATHENA_OUTPUT_BUCKET = 'staging-data-ingestion-bucket'; + +const limiter = new Bottleneck({ + maxConcurrent: 5, + minTime: 100, +}); + +type QueryResult = Record; + +const parseRow = (row: Row, columnInfo: ColumnInfo[]): QueryResult => { + const rowData = row.Data; + if (!rowData) { + return {}; + } + + return columnInfo.reduce((acc: QueryResult, column, index) => { + if (column.Name) { + acc[column.Name] = rowData[index]?.VarCharValue ?? null; + } + return acc; + }, {}); +}; + +const athena = new AthenaClient({ + region: process.env.AWS_REGION, + retryStrategy: new ConfiguredRetryStrategy(3, (attempt: number) => 100 + attempt * 1000), +}); + +export const Athena = ({ + overrideDatabaseName, + overrideBucketName, +}: { overrideDatabaseName?: string; overrideBucketName?: string } = {}) => { + const database = overrideDatabaseName ?? process.env.ATHENA_DATABASE ?? DEFAULT_ATHENA_DATABASE; + const outputLocation = `s3://${ + overrideBucketName ?? process.env.ATHENA_OUTPUT_BUCKET ?? DEFAULT_ATHENA_OUTPUT_BUCKET + }/athena`; + + const startQuery = async (query: string, params?: Record) => { + const executionParams: StartQueryExecutionCommandInput = { + QueryString: query, + QueryExecutionContext: { + Database: database, + }, + ResultConfiguration: { + OutputLocation: outputLocation, + }, + ResultReuseConfiguration: { + ResultReuseByAgeConfiguration: { + Enabled: false, + }, + }, + }; + + if (params) { + executionParams.ExecutionParameters = Object.values(params); + } + + const { QueryExecutionId } = await athena.send( + new StartQueryExecutionCommand(executionParams) + ); + + if (!QueryExecutionId) { + throw new Error('Failed to start query execution'); + } + + return QueryExecutionId; + }; + + const getQueryExecution = async (queryExecutionId: string) => { + const { QueryExecution } = await athena.send( + new GetQueryExecutionCommand({ + QueryExecutionId: queryExecutionId, + }) + ); + + return QueryExecution; + }; + + const waitForQueryCompletion = async ( + queryExecutionId: string, + timeout: number = QUERY_TIMEOUT + ): Promise => { + const startTime = Date.now(); + + while (Date.now() - startTime < timeout) { + const execution = await getQueryExecution(queryExecutionId); + logger.info( + `Total bytes scanned: ${(execution?.Statistics?.DataScannedInBytes ?? 0) / 1024}kb` + ); + const state = execution?.Status?.State; + + switch (state) { + case QueryExecutionState.SUCCEEDED: + return; + case QueryExecutionState.FAILED: + throw new Error(`Query failed: ${execution?.Status?.StateChangeReason}`); + case QueryExecutionState.CANCELLED: + throw new Error('Query was cancelled'); + default: + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL)); + } + } + + throw new Error(`Query timeout after ${timeout}ms`); + }; + + const getQueryResults = async ( + queryExecutionId: string, + nextToken?: string + ): Promise => { + return athena.send( + new GetQueryResultsCommand({ + QueryExecutionId: queryExecutionId, + NextToken: nextToken, + }) + ); + }; + + const getAllQueryResults = async (queryExecutionId: string): Promise => { + let nextToken: string | undefined; + const allResults: QueryResult[] = []; + + do { + const results = await getQueryResults(queryExecutionId, nextToken); + if (results.ResultSet?.Rows && results.ResultSet.ResultSetMetadata?.ColumnInfo) { + const columnInfo = results.ResultSet.ResultSetMetadata.ColumnInfo; + const rows = nextToken ? results.ResultSet.Rows : results.ResultSet.Rows.slice(1); + const parsedRows = rows.map((row) => parseRow(row, columnInfo)); + allResults.push(...parsedRows); + } + + nextToken = results.NextToken; + } while (nextToken); + + return allResults; + }; + + const query = async ( + queryString: string, + params?: Record + ): Promise => { + try { + const queryExecutionId = await startQuery(queryString, params); + await waitForQueryCompletion(queryExecutionId); + return getAllQueryResults(queryExecutionId); + } catch (error) { + const { message } = error as Error; + logger.error(`Error executing Athena query: ${message}`); + throw error; + } + }; + + const batchQuery = async ({ + queries, + }: { + queries: { query: string; params?: Record }[]; + }): Promise<{ + results: QueryResult[][]; + errors: Error[]; + }> => { + const results: QueryResult[][] = []; + const errors: Error[] = []; + + await Promise.all( + queries.map(async ({ query: queryString, params }, index) => { + try { + const result = await limiter.schedule(() => query(queryString, params)); + results[index] = result; + } catch (error) { + logger.error(`Error in batch query ${index}: ${queryString}, Error: ${error}`); + errors[index] = error as Error; + } + }) + ); + + if (errors.length > 0) { + logger.warn(`${errors.length} queries failed in batch execution`); + } + + return { + results, + errors, + }; + }; + + return { + query, + batchQuery, + startQuery, + getQueryExecution, + getQueryResults, + getAllQueryResults, + waitForQueryCompletion, + }; +}; + diff --git a/src/athena/index.ts b/src/athena/index.ts new file mode 100644 index 0000000..e9fb402 --- /dev/null +++ b/src/athena/index.ts @@ -0,0 +1,4 @@ +export * from './client'; +export * from './utils'; +export * from './repositories/fillQualityAnalytics'; + diff --git a/src/athena/repositories/fillQualityAnalytics.ts b/src/athena/repositories/fillQualityAnalytics.ts new file mode 100644 index 0000000..03c75ab --- /dev/null +++ b/src/athena/repositories/fillQualityAnalytics.ts @@ -0,0 +1,280 @@ +import { Athena } from '../client'; + +export interface TakerFillVsOracleBpsResult { + Time: string; + MarketIndex: string; + TakerBuyBpsFromOracle_ALL: string | null; + TakerSellBpsFromOracle_ALL: string | null; + TakerBuyBpsFromOracle_1e0: string | null; + TakerBuyBpsFromOracle_1e3: string | null; + TakerBuyBpsFromOracle_1e4: string | null; + TakerBuyBpsFromOracle_1e5: string | null; + TakerBuyBpsFromOracle_1e6: string | null; + TakerSellBpsFromOracle_1e0: string | null; + TakerSellBpsFromOracle_1e3: string | null; + TakerSellBpsFromOracle_1e4: string | null; + TakerSellBpsFromOracle_1e5: string | null; + TakerSellBpsFromOracle_1e6: string | null; + Zero: string; +} + +export const FillQualityAnalyticsRepository = () => { + const { query } = Athena(); + + /** + * Get taker fill vs oracle basis points, bucketed by order notional cohorts at place-time size. + * Returns a rolling average per cohort/direction, partitioned by market. + * This query returns data for ALL perp markets in a single result set. + * + * @param from - Unix timestamp in milliseconds + * @param to - Unix timestamp in milliseconds + * @param baseDecimals - Base decimals for the market (default: 9) + * @param smoothingMinutes - Minutes for rolling average (default: 60) + * @returns Array of time series data with taker fill vs oracle bps by cohort and market + */ + const getTakerFillVsOracleBps = async ( + fromMs: number, + toMs: number, + baseDecimals: number = 9, + smoothingMinutes: number = 60 + ): Promise => { + // Taker fill vs Oracle bps, bucketed by order notional cohorts at place-time size + // Final output: latest non-null rolling average per cohort/direction, PARTITIONED BY market + const queryString = ` +WITH +from_dt AS ( + SELECT + date_format(from_unixtime(CAST(${fromMs}/1000 AS BIGINT)) AT TIME ZONE 'UTC','%Y') AS yf, + date_format(from_unixtime(CAST(${fromMs}/1000 AS BIGINT)) AT TIME ZONE 'UTC','%m') AS mf, + date_format(from_unixtime(CAST(${fromMs}/1000 AS BIGINT)) AT TIME ZONE 'UTC','%d') AS df +), +to_dt AS ( + SELECT + date_format(from_unixtime(CAST(${toMs}/1000 AS BIGINT)) AT TIME ZONE 'UTC','%Y') AS yt, + date_format(from_unixtime(CAST(${toMs}/1000 AS BIGINT)) AT TIME ZONE 'UTC','%m') AS mt, + date_format(from_unixtime(CAST(${toMs}/1000 AS BIGINT)) AT TIME ZONE 'UTC','%d') AS dt +), + +-- Dedup to the earliest OrderRecord per (txsig,user,orderid,marketindex) to reflect "place" state +orders_dedup AS ( + SELECT * + FROM ( + SELECT + CAST(orx.ts AS BIGINT) AS order_ts_epoch, + orx.txsig AS txsig, + orx.user AS user, + orx."order".orderid AS orderid, + orx."order".marketindex AS marketindex, + CAST(orx."order".baseassetamount AS DOUBLE) AS base_asset_amount_raw, + ROW_NUMBER() OVER ( + PARTITION BY orx.txsig, orx.user, orx."order".orderid, orx."order".marketindex + ORDER BY CAST(orx.ts AS BIGINT) ASC + ) AS rn + FROM eventtype_orderrecord orx + CROSS JOIN from_dt f + CROSS JOIN to_dt t + WHERE + orx."order".markettype = 'perp' + -- UTC-safe pruning on VARCHAR partitions + AND orx.year BETWEEN f.yf AND t.yt + AND ( + orx.year > f.yf + OR (orx.year = f.yf AND orx.month > f.mf) + OR (orx.year = f.yf AND orx.month = f.mf AND orx.day >= f.df) + ) + AND ( + orx.year < t.yt + OR (orx.year = t.yt AND orx.month < t.mt) + OR (orx.year = t.yt AND orx.month = t.mt AND orx.day <= t.dt) + ) + -- absolute time guardrails (ts is epoch seconds in table) + AND CAST(orx.ts AS BIGINT) BETWEEN CAST(${fromMs}/1000 AS BIGINT) AND CAST(${toMs}/1000 AS BIGINT) + ) t1 + WHERE rn = 1 +), + +-- Trade fills (taker perspective) within the window (all markets) +trades AS ( + SELECT + CAST(tr.ts AS BIGINT) AS ts_epoch, + tr.txsig AS txsig, + tr.taker AS user, + tr.takerorderid AS orderid, + tr.marketindex AS marketindex, + LOWER(tr.takerorderdirection) AS dir, + CAST(tr.quoteassetamountfilled AS DOUBLE) AS q_filled, + CAST(tr.baseassetamountfilled AS DOUBLE) AS b_filled, + CAST(tr.oracleprice AS DOUBLE) AS oracle_raw + FROM eventtype_traderecord tr + CROSS JOIN from_dt f + CROSS JOIN to_dt t + WHERE + LOWER(tr.action) = 'fill' + AND tr.markettype = 'perp' + AND LOWER(tr.takerorderdirection) IN ('long','short') + + -- UTC-safe pruning on VARCHAR partitions + AND tr.year BETWEEN f.yf AND t.yt + AND ( + tr.year > f.yf + OR (tr.year = f.yf AND tr.month > f.mf) + OR (tr.year = f.yf AND tr.month = f.mf AND tr.day >= f.df) + ) + AND ( + tr.year < t.yt + OR (tr.year = t.yt AND tr.month < t.mt) + OR (tr.year = t.yt AND tr.month = t.mt AND tr.day <= t.dt) + ) + + -- absolute time guardrails (ts is epoch seconds in table) + AND CAST(tr.ts AS BIGINT) BETWEEN CAST(${fromMs}/1000 AS BIGINT) AND CAST(${toMs}/1000 AS BIGINT) +), + +-- Join fills to the order's initial size; compute notional cohort and taker bps from oracle +joined AS ( + SELECT + from_unixtime(tr.ts_epoch) AS Time, + tr.marketindex AS MarketIndex, + tr.dir AS dir, + ( + ( + (tr.q_filled / NULLIF(tr.b_filled, 0)) + * pow(10.0, (${baseDecimals} - 6)) + ) + / (tr.oracle_raw / 1e6) - 1.0 + ) * 10000.0 AS taker_bps_from_oracle, + ( (od.base_asset_amount_raw * 1e-9) * (tr.oracle_raw * 1e-6) ) AS order_value + FROM trades tr + JOIN orders_dedup od + ON od.txsig = tr.txsig + AND od.user = tr.user + AND od.orderid = tr.orderid + AND od.marketindex = tr.marketindex +), + +-- Bucket into cohorts +bucketed AS ( + SELECT + Time, + MarketIndex, + dir, + taker_bps_from_oracle, + CASE + WHEN order_value > 0 AND order_value < 1000 THEN '1e0' + WHEN order_value >= 1000 AND order_value < 10000 THEN '1e3' + WHEN order_value >= 10000 AND order_value < 100000 THEN '1e4' + WHEN order_value >= 100000 AND order_value < 1000000 THEN '1e5' + WHEN order_value >= 1000000 THEN '1e6' + ELSE 'other' + END AS cohort + FROM joined +), + +-- Compute rolling averages +rolling_avgs AS ( + SELECT + Time, + MarketIndex, + + -- Non-cohort series (ALL fills regardless of cohort) + AVG(CASE WHEN dir = 'long' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerBuyBpsFromOracle_ALL", + + AVG(CASE WHEN dir = 'short' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerSellBpsFromOracle_ALL", + + -- rolling average by cohort/direction (NULLs ignored by AVG) + AVG(CASE WHEN dir = 'long' AND cohort = '1e0' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerBuyBpsFromOracle_1e0", + + AVG(CASE WHEN dir = 'long' AND cohort = '1e3' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerBuyBpsFromOracle_1e3", + + AVG(CASE WHEN dir = 'long' AND cohort = '1e4' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerBuyBpsFromOracle_1e4", + + AVG(CASE WHEN dir = 'long' AND cohort = '1e5' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerBuyBpsFromOracle_1e5", + + AVG(CASE WHEN dir = 'long' AND cohort = '1e6' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerBuyBpsFromOracle_1e6", + + AVG(CASE WHEN dir = 'short' AND cohort = '1e0' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerSellBpsFromOracle_1e0", + + AVG(CASE WHEN dir = 'short' AND cohort = '1e3' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerSellBpsFromOracle_1e3", + + AVG(CASE WHEN dir = 'short' AND cohort = '1e4' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerSellBpsFromOracle_1e4", + + AVG(CASE WHEN dir = 'short' AND cohort = '1e5' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerSellBpsFromOracle_1e5", + + AVG(CASE WHEN dir = 'short' AND cohort = '1e6' THEN taker_bps_from_oracle END) + OVER (PARTITION BY MarketIndex ORDER BY Time RANGE BETWEEN INTERVAL '${smoothingMinutes}' MINUTE PRECEDING AND CURRENT ROW) + AS "TakerSellBpsFromOracle_1e6", + + ROW_NUMBER() OVER (PARTITION BY MarketIndex ORDER BY Time DESC) AS rn + FROM bucketed + WHERE taker_bps_from_oracle IS NOT NULL +) + +-- Select only the latest row per market +SELECT + Time, + MarketIndex, + "TakerBuyBpsFromOracle_ALL", + "TakerSellBpsFromOracle_ALL", + "TakerBuyBpsFromOracle_1e0", + "TakerBuyBpsFromOracle_1e3", + "TakerBuyBpsFromOracle_1e4", + "TakerBuyBpsFromOracle_1e5", + "TakerBuyBpsFromOracle_1e6", + "TakerSellBpsFromOracle_1e0", + "TakerSellBpsFromOracle_1e3", + "TakerSellBpsFromOracle_1e4", + "TakerSellBpsFromOracle_1e5", + "TakerSellBpsFromOracle_1e6" +FROM rolling_avgs +WHERE rn = 1 +ORDER BY MarketIndex; + `; + + const results = await query(queryString); + + return results.map((result) => ({ + Time: result.Time || '', + MarketIndex: result.MarketIndex || '', + TakerBuyBpsFromOracle_ALL: result.TakerBuyBpsFromOracle_ALL, + TakerSellBpsFromOracle_ALL: result.TakerSellBpsFromOracle_ALL, + TakerBuyBpsFromOracle_1e0: result.TakerBuyBpsFromOracle_1e0, + TakerBuyBpsFromOracle_1e3: result.TakerBuyBpsFromOracle_1e3, + TakerBuyBpsFromOracle_1e4: result.TakerBuyBpsFromOracle_1e4, + TakerBuyBpsFromOracle_1e5: result.TakerBuyBpsFromOracle_1e5, + TakerBuyBpsFromOracle_1e6: result.TakerBuyBpsFromOracle_1e6, + TakerSellBpsFromOracle_1e0: result.TakerSellBpsFromOracle_1e0, + TakerSellBpsFromOracle_1e3: result.TakerSellBpsFromOracle_1e3, + TakerSellBpsFromOracle_1e4: result.TakerSellBpsFromOracle_1e4, + TakerSellBpsFromOracle_1e5: result.TakerSellBpsFromOracle_1e5, + TakerSellBpsFromOracle_1e6: result.TakerSellBpsFromOracle_1e6, + Zero: result.Zero || '0', + })); + }; + + return { + getTakerFillVsOracleBps, + }; +}; + diff --git a/src/athena/utils.ts b/src/athena/utils.ts new file mode 100644 index 0000000..484ce10 --- /dev/null +++ b/src/athena/utils.ts @@ -0,0 +1,40 @@ +export const getTimePartition = (from: number, to: number) => { + return `WITH time_range AS ( + SELECT + ${from} as from_ts, + ${to} as to_ts, + DATE_FORMAT(from_unixtime(${from}), '%Y%m%d') as from_date, + DATE_FORMAT(from_unixtime(${to}), '%Y%m%d') as to_date + )`; +}; + +export const getTimeRangeAndPartitions = ( + from: number, + to: number, + valid_partitions_label = 'v' +) => { + const dates: { year: string; month: string; day: string }[] = []; + const fromDate = new Date(from * 1000); + const toDate = new Date(to * 1000); + + for (let d = fromDate; d <= toDate; d.setDate(d.getDate() + 1)) { + dates.push({ + year: d.getUTCFullYear().toString(), + month: (d.getUTCMonth() + 1).toString().padStart(2, '0'), + day: d.getUTCDate().toString().padStart(2, '0'), + }); + } + + return `WITH time_range AS ( + SELECT + ${from} AS from_ts, + ${to} AS to_ts + ), + + valid_partitions AS ( + SELECT * FROM (VALUES + ${dates.map((d) => `('${d.year}', '${d.month}', '${d.day}')`).join(', ')} + ) AS ${valid_partitions_label}(year, month, day) + )`; +}; + From a627c89e0ae55f4bb8f6b84011726af015252a72 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 20 Oct 2025 18:37:36 -0700 Subject: [PATCH 2/4] update package.json --- package.json | 1 + yarn.lock | 761 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 760 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d490579..a6b141e 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "lib/index.js", "license": "Apache-2.0", "dependencies": { + "@aws-sdk/client-athena": "^3.830.0", "@aws-sdk/client-kinesis": "^3.830.0", "@aws-sdk/util-retry": "^3.374.0", "@coral-xyz/anchor": "^0.29.0", diff --git a/yarn.lock b/yarn.lock index a0525c9..932b52a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -57,6 +57,52 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" +"@aws-sdk/client-athena@^3.830.0": + version "3.913.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-athena/-/client-athena-3.913.0.tgz#802e8a62a925681b54a89714d4c48decf45a36f7" + integrity sha512-OCiUqQ1iuSWQb3SHkYPbrzP2s8kzrt+NdNnNM1+C1fiEgY+eWxnxdQXw+pW7ZzJZOged2tGwuG7O2Jl5RhQtrw== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.911.0" + "@aws-sdk/credential-provider-node" "3.913.0" + "@aws-sdk/middleware-host-header" "3.910.0" + "@aws-sdk/middleware-logger" "3.910.0" + "@aws-sdk/middleware-recursion-detection" "3.910.0" + "@aws-sdk/middleware-user-agent" "3.911.0" + "@aws-sdk/region-config-resolver" "3.910.0" + "@aws-sdk/types" "3.910.0" + "@aws-sdk/util-endpoints" "3.910.0" + "@aws-sdk/util-user-agent-browser" "3.910.0" + "@aws-sdk/util-user-agent-node" "3.911.0" + "@smithy/config-resolver" "^4.3.2" + "@smithy/core" "^3.16.1" + "@smithy/fetch-http-handler" "^5.3.3" + "@smithy/hash-node" "^4.2.2" + "@smithy/invalid-dependency" "^4.2.2" + "@smithy/middleware-content-length" "^4.2.2" + "@smithy/middleware-endpoint" "^4.3.3" + "@smithy/middleware-retry" "^4.4.3" + "@smithy/middleware-serde" "^4.2.2" + "@smithy/middleware-stack" "^4.2.2" + "@smithy/node-config-provider" "^4.3.2" + "@smithy/node-http-handler" "^4.4.1" + "@smithy/protocol-http" "^5.3.2" + "@smithy/smithy-client" "^4.8.1" + "@smithy/types" "^4.7.1" + "@smithy/url-parser" "^4.2.2" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-body-length-node" "^4.2.1" + "@smithy/util-defaults-mode-browser" "^4.3.2" + "@smithy/util-defaults-mode-node" "^4.2.3" + "@smithy/util-endpoints" "^3.2.2" + "@smithy/util-middleware" "^4.2.2" + "@smithy/util-retry" "^4.2.2" + "@smithy/util-utf8" "^4.2.0" + "@smithy/uuid" "^1.1.0" + tslib "^2.6.2" + "@aws-sdk/client-kinesis@^3.830.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-kinesis/-/client-kinesis-3.844.0.tgz#ab20007ebe5276e85aca646d9fbeb2e2e9b09269" @@ -150,6 +196,50 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/client-sso@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.911.0.tgz#fdf4c3ade5b464dfc23d6ab54184757293ddbbec" + integrity sha512-N9QAeMvN3D1ZyKXkQp4aUgC4wUMuA5E1HuVCkajc0bq1pnH4PIke36YlrDGGREqPlyLFrXCkws2gbL5p23vtlg== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.911.0" + "@aws-sdk/middleware-host-header" "3.910.0" + "@aws-sdk/middleware-logger" "3.910.0" + "@aws-sdk/middleware-recursion-detection" "3.910.0" + "@aws-sdk/middleware-user-agent" "3.911.0" + "@aws-sdk/region-config-resolver" "3.910.0" + "@aws-sdk/types" "3.910.0" + "@aws-sdk/util-endpoints" "3.910.0" + "@aws-sdk/util-user-agent-browser" "3.910.0" + "@aws-sdk/util-user-agent-node" "3.911.0" + "@smithy/config-resolver" "^4.3.2" + "@smithy/core" "^3.16.1" + "@smithy/fetch-http-handler" "^5.3.3" + "@smithy/hash-node" "^4.2.2" + "@smithy/invalid-dependency" "^4.2.2" + "@smithy/middleware-content-length" "^4.2.2" + "@smithy/middleware-endpoint" "^4.3.3" + "@smithy/middleware-retry" "^4.4.3" + "@smithy/middleware-serde" "^4.2.2" + "@smithy/middleware-stack" "^4.2.2" + "@smithy/node-config-provider" "^4.3.2" + "@smithy/node-http-handler" "^4.4.1" + "@smithy/protocol-http" "^5.3.2" + "@smithy/smithy-client" "^4.8.1" + "@smithy/types" "^4.7.1" + "@smithy/url-parser" "^4.2.2" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-body-length-node" "^4.2.1" + "@smithy/util-defaults-mode-browser" "^4.3.2" + "@smithy/util-defaults-mode-node" "^4.2.3" + "@smithy/util-endpoints" "^3.2.2" + "@smithy/util-middleware" "^4.2.2" + "@smithy/util-retry" "^4.2.2" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/core@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.844.0.tgz#de86e6cafd348e2dd8c865232de4fa768472f6cb" @@ -171,6 +261,25 @@ fast-xml-parser "5.2.5" tslib "^2.6.2" +"@aws-sdk/core@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.911.0.tgz#e41615add824be8a4deb9daaed626f5f1e467a12" + integrity sha512-k4QG9A+UCq/qlDJFmjozo6R0eXXfe++/KnCDMmajehIE9kh+b/5DqlGvAmbl9w4e92LOtrY6/DN3mIX1xs4sXw== + dependencies: + "@aws-sdk/types" "3.910.0" + "@aws-sdk/xml-builder" "3.911.0" + "@smithy/core" "^3.16.1" + "@smithy/node-config-provider" "^4.3.2" + "@smithy/property-provider" "^4.2.2" + "@smithy/protocol-http" "^5.3.2" + "@smithy/signature-v4" "^5.3.2" + "@smithy/smithy-client" "^4.8.1" + "@smithy/types" "^4.7.1" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-middleware" "^4.2.2" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-env@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.844.0.tgz#8a23486c1e333b83de2669e2bba89beee258aa57" @@ -182,6 +291,17 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-env@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.911.0.tgz#4ff8060cb7dd5207580c415c97e44ddcfede562a" + integrity sha512-6FWRwWn3LUZzLhqBXB+TPMW2ijCWUqGICSw8bVakEdODrvbiv1RT/MVUayzFwz/ek6e6NKZn6DbSWzx07N9Hjw== + dependencies: + "@aws-sdk/core" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/property-provider" "^4.2.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-http@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.844.0.tgz#61cdf1a6e50f1ad132764ca314fddc0dc355c6dc" @@ -198,6 +318,22 @@ "@smithy/util-stream" "^4.2.3" tslib "^2.6.2" +"@aws-sdk/credential-provider-http@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.911.0.tgz#c8f6aed9a7be8162c411aa8af417fb5b008d8fd4" + integrity sha512-xUlwKmIUW2fWP/eM3nF5u4CyLtOtyohlhGJ5jdsJokr3MrQ7w0tDITO43C9IhCn+28D5UbaiWnKw5ntkw7aVfA== + dependencies: + "@aws-sdk/core" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/fetch-http-handler" "^5.3.3" + "@smithy/node-http-handler" "^4.4.1" + "@smithy/property-provider" "^4.2.2" + "@smithy/protocol-http" "^5.3.2" + "@smithy/smithy-client" "^4.8.1" + "@smithy/types" "^4.7.1" + "@smithy/util-stream" "^4.5.2" + tslib "^2.6.2" + "@aws-sdk/credential-provider-ini@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.844.0.tgz#02475e5205dad0bb99e2c876c2e7fc1ef3de426b" @@ -217,6 +353,25 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-ini@3.913.0": + version "3.913.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.913.0.tgz#ef62c145588889d43c03f47042c5dfe60be2681b" + integrity sha512-iR4c4NQ1OSRKQi0SxzpwD+wP1fCy+QNKtEyCajuVlD0pvmoIHdrm5THK9e+2/7/SsQDRhOXHJfLGxHapD74WJw== + dependencies: + "@aws-sdk/core" "3.911.0" + "@aws-sdk/credential-provider-env" "3.911.0" + "@aws-sdk/credential-provider-http" "3.911.0" + "@aws-sdk/credential-provider-process" "3.911.0" + "@aws-sdk/credential-provider-sso" "3.911.0" + "@aws-sdk/credential-provider-web-identity" "3.911.0" + "@aws-sdk/nested-clients" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/credential-provider-imds" "^4.2.2" + "@smithy/property-provider" "^4.2.2" + "@smithy/shared-ini-file-loader" "^4.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-node@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.844.0.tgz#5a033ac75dd8171e70ade558ed20fbe4bc69014d" @@ -235,6 +390,24 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-node@3.913.0": + version "3.913.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.913.0.tgz#5eb4242f671a889869cba4c571adb832daf7b615" + integrity sha512-HQPLkKDxS83Q/nZKqg9bq4igWzYQeOMqhpx5LYs4u1GwsKeCsYrrfz12Iu4IHNWPp9EnGLcmdfbfYuqZGrsaSQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.911.0" + "@aws-sdk/credential-provider-http" "3.911.0" + "@aws-sdk/credential-provider-ini" "3.913.0" + "@aws-sdk/credential-provider-process" "3.911.0" + "@aws-sdk/credential-provider-sso" "3.911.0" + "@aws-sdk/credential-provider-web-identity" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/credential-provider-imds" "^4.2.2" + "@smithy/property-provider" "^4.2.2" + "@smithy/shared-ini-file-loader" "^4.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-process@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.844.0.tgz#80adcd929b41c9ddd8762ef1abdd799c45c1ea73" @@ -247,6 +420,18 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-process@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.911.0.tgz#61a671487e1808c7206858e83b5e487352507ea2" + integrity sha512-mKshhV5jRQffZjbK9x7bs+uC2IsYKfpzYaBamFsEov3xtARCpOiKaIlM8gYKFEbHT2M+1R3rYYlhhl9ndVWS2g== + dependencies: + "@aws-sdk/core" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/property-provider" "^4.2.2" + "@smithy/shared-ini-file-loader" "^4.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-sso@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.844.0.tgz#99431c2a81f7dec8a461512d60cf7cd17e9e1264" @@ -261,6 +446,20 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-sso@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.911.0.tgz#562a31e66e5aadb841f03d02d2da9e96b7a595d9" + integrity sha512-JAxd4uWe0Zc9tk6+N0cVxe9XtJVcOx6Ms0k933ZU9QbuRMH6xti/wnZxp/IvGIWIDzf5fhqiGyw5MSyDeI5b1w== + dependencies: + "@aws-sdk/client-sso" "3.911.0" + "@aws-sdk/core" "3.911.0" + "@aws-sdk/token-providers" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/property-provider" "^4.2.2" + "@smithy/shared-ini-file-loader" "^4.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-web-identity@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.844.0.tgz#0c38cc1f1fa9e3a9d73ac3e356b87c0a1bb41eaa" @@ -273,6 +472,19 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-web-identity@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.911.0.tgz#7d848ee429991bb2130e541ff7d582dce740d03c" + integrity sha512-urIbXWWG+cm54RwwTFQuRwPH0WPsMFSDF2/H9qO2J2fKoHRURuyblFCyYG3aVKZGvFBhOizJYexf5+5w3CJKBw== + dependencies: + "@aws-sdk/core" "3.911.0" + "@aws-sdk/nested-clients" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/property-provider" "^4.2.2" + "@smithy/shared-ini-file-loader" "^4.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/middleware-host-header@3.840.0": version "3.840.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz#7c8b163fb13d588b87523b53f7d98de73262e83f" @@ -283,6 +495,16 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/middleware-host-header@3.910.0": + version "3.910.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.910.0.tgz#5f1c11668c2d2be55253e886b9b622e188314f7b" + integrity sha512-F9Lqeu80/aTM6S/izZ8RtwSmjfhWjIuxX61LX+/9mxJyEkgaECRxv0chsLQsLHJumkGnXRy/eIyMLBhcTPF5vg== + dependencies: + "@aws-sdk/types" "3.910.0" + "@smithy/protocol-http" "^5.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/middleware-logger@3.840.0": version "3.840.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz#d92ade1817ac7dc78a3567c1239bb1a3f3b1b57a" @@ -292,6 +514,15 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/middleware-logger@3.910.0": + version "3.910.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.910.0.tgz#ebe29050cfd2fdfb6a2d2cb5a829d53dc82dfc14" + integrity sha512-3LJyyfs1USvRuRDla1pGlzGRtXJBXD1zC9F+eE9Iz/V5nkmhyv52A017CvKWmYoR0DM9dzjLyPOI0BSSppEaTw== + dependencies: + "@aws-sdk/types" "3.910.0" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/middleware-recursion-detection@3.840.0": version "3.840.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz#8ea2c00af258db0b64ea394e044cedb6101b5ffd" @@ -302,6 +533,17 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/middleware-recursion-detection@3.910.0": + version "3.910.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.910.0.tgz#ea901901b633ec8b76e8144f0ae29c4f917e8a04" + integrity sha512-m/oLz0EoCy+WoIVBnXRXJ4AtGpdl0kPE7U+VH9TsuUzHgxY1Re/176Q1HWLBRVlz4gr++lNsgsMWEC+VnAwMpw== + dependencies: + "@aws-sdk/types" "3.910.0" + "@aws/lambda-invoke-store" "^0.0.1" + "@smithy/protocol-http" "^5.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/middleware-user-agent@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.844.0.tgz#3d1dc03c5271930b5faf1c3a52ce7a8eae3711f4" @@ -315,6 +557,19 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/middleware-user-agent@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.911.0.tgz#51f8c9790401385da97988b3e94b88c662b7bad4" + integrity sha512-rY3LvGvgY/UI0nmt5f4DRzjEh8135A2TeHcva1bgOmVfOI4vkkGfA20sNRqerOkSO6hPbkxJapO50UJHFzmmyA== + dependencies: + "@aws-sdk/core" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@aws-sdk/util-endpoints" "3.910.0" + "@smithy/core" "^3.16.1" + "@smithy/protocol-http" "^5.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/nested-clients@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.844.0.tgz#bc547bbbe04b4331812b5aff86632ff0e91fa1a5" @@ -359,6 +614,50 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@aws-sdk/nested-clients@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.911.0.tgz#0b4a8662f3da24f0dcc6d5c4d298465a5deb1a7b" + integrity sha512-lp/sXbdX/S0EYaMYPVKga0omjIUbNNdFi9IJITgKZkLC6CzspihIoHd5GIdl4esMJevtTQQfkVncXTFkf/a4YA== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.911.0" + "@aws-sdk/middleware-host-header" "3.910.0" + "@aws-sdk/middleware-logger" "3.910.0" + "@aws-sdk/middleware-recursion-detection" "3.910.0" + "@aws-sdk/middleware-user-agent" "3.911.0" + "@aws-sdk/region-config-resolver" "3.910.0" + "@aws-sdk/types" "3.910.0" + "@aws-sdk/util-endpoints" "3.910.0" + "@aws-sdk/util-user-agent-browser" "3.910.0" + "@aws-sdk/util-user-agent-node" "3.911.0" + "@smithy/config-resolver" "^4.3.2" + "@smithy/core" "^3.16.1" + "@smithy/fetch-http-handler" "^5.3.3" + "@smithy/hash-node" "^4.2.2" + "@smithy/invalid-dependency" "^4.2.2" + "@smithy/middleware-content-length" "^4.2.2" + "@smithy/middleware-endpoint" "^4.3.3" + "@smithy/middleware-retry" "^4.4.3" + "@smithy/middleware-serde" "^4.2.2" + "@smithy/middleware-stack" "^4.2.2" + "@smithy/node-config-provider" "^4.3.2" + "@smithy/node-http-handler" "^4.4.1" + "@smithy/protocol-http" "^5.3.2" + "@smithy/smithy-client" "^4.8.1" + "@smithy/types" "^4.7.1" + "@smithy/url-parser" "^4.2.2" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-body-length-node" "^4.2.1" + "@smithy/util-defaults-mode-browser" "^4.3.2" + "@smithy/util-defaults-mode-node" "^4.2.3" + "@smithy/util-endpoints" "^3.2.2" + "@smithy/util-middleware" "^4.2.2" + "@smithy/util-retry" "^4.2.2" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/region-config-resolver@3.840.0": version "3.840.0" resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz#240690ead3131c4c47186b4929776439fe2f6729" @@ -371,6 +670,18 @@ "@smithy/util-middleware" "^4.0.4" tslib "^2.6.2" +"@aws-sdk/region-config-resolver@3.910.0": + version "3.910.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.910.0.tgz#31f365a3bc254e4c4417a39d0f40338acdbe3da4" + integrity sha512-gzQAkuHI3xyG6toYnH/pju+kc190XmvnB7X84vtN57GjgdQJICt9So/BD0U6h+eSfk9VBnafkVrAzBzWMEFZVw== + dependencies: + "@aws-sdk/types" "3.910.0" + "@smithy/node-config-provider" "^4.3.2" + "@smithy/types" "^4.7.1" + "@smithy/util-config-provider" "^4.2.0" + "@smithy/util-middleware" "^4.2.2" + tslib "^2.6.2" + "@aws-sdk/token-providers@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.844.0.tgz#acb1ec53999a488498cb656d35338f3652dcbd69" @@ -384,6 +695,19 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/token-providers@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.911.0.tgz#16d41de62fbed6e9a3b3ae2bc2ced0be83a982b4" + integrity sha512-O1c5F1pbEImgEe3Vr8j1gpWu69UXWj3nN3vvLGh77hcrG5dZ8I27tSP5RN4Labm8Dnji/6ia+vqSYpN8w6KN5A== + dependencies: + "@aws-sdk/core" "3.911.0" + "@aws-sdk/nested-clients" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/property-provider" "^4.2.2" + "@smithy/shared-ini-file-loader" "^4.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/types@3.840.0", "@aws-sdk/types@^3.222.0": version "3.840.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.840.0.tgz#aadc6843d5c1f24b3d1d228059e702a355bf07c3" @@ -392,6 +716,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/types@3.910.0": + version "3.910.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.910.0.tgz#1707a9f5d36d828789173fcd5622a5684cf67b2f" + integrity sha512-o67gL3vjf4nhfmuSUNNkit0d62QJEwwHLxucwVJkR/rw9mfUtAWsgBs8Tp16cdUbMgsyQtCQilL8RAJDoGtadQ== + dependencies: + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/util-endpoints@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.844.0.tgz#3c1fef4d4cc00641fc01092379509638b75dd84e" @@ -403,6 +735,17 @@ "@smithy/util-endpoints" "^3.0.6" tslib "^2.6.2" +"@aws-sdk/util-endpoints@3.910.0": + version "3.910.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.910.0.tgz#aca087159fa0c89d514e534b369724bea4211bd3" + integrity sha512-6XgdNe42ibP8zCQgNGDWoOF53RfEKzpU/S7Z29FTTJ7hcZv0SytC0ZNQQZSx4rfBl036YWYwJRoJMlT4AA7q9A== + dependencies: + "@aws-sdk/types" "3.910.0" + "@smithy/types" "^4.7.1" + "@smithy/url-parser" "^4.2.2" + "@smithy/util-endpoints" "^3.2.2" + tslib "^2.6.2" + "@aws-sdk/util-locate-window@^3.0.0": version "3.804.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz#a2ee8dc5d9c98276986e8e1ba03c0c84d9afb0f5" @@ -428,6 +771,16 @@ bowser "^2.11.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-browser@3.910.0": + version "3.910.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.910.0.tgz#93b4ca9c9b3675b3e6d95c9ef517ac2db159e131" + integrity sha512-iOdrRdLZHrlINk9pezNZ82P/VxO/UmtmpaOAObUN+xplCUJu31WNM2EE/HccC8PQw6XlAudpdA6HDTGiW6yVGg== + dependencies: + "@aws-sdk/types" "3.910.0" + "@smithy/types" "^4.7.1" + bowser "^2.11.0" + tslib "^2.6.2" + "@aws-sdk/util-user-agent-node@3.844.0": version "3.844.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.844.0.tgz#3dd445708d3794584e76114bbfe4ea468eb5380e" @@ -439,6 +792,17 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/util-user-agent-node@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.911.0.tgz#71c65decf363cffc37674f8cc6abd65ae169ae03" + integrity sha512-3l+f6ooLF6Z6Lz0zGi7vSKSUYn/EePPizv88eZQpEAFunBHv+CSVNPtxhxHfkm7X9tTsV4QGZRIqo3taMLolmA== + dependencies: + "@aws-sdk/middleware-user-agent" "3.911.0" + "@aws-sdk/types" "3.910.0" + "@smithy/node-config-provider" "^4.3.2" + "@smithy/types" "^4.7.1" + tslib "^2.6.2" + "@aws-sdk/xml-builder@3.821.0": version "3.821.0" resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz#ff89bf1276fca41276ed508b9c8ae21978d91177" @@ -447,6 +811,20 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@aws-sdk/xml-builder@3.911.0": + version "3.911.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.911.0.tgz#adcf71c4390ce3bb5ecb921eb45aa72830d7e361" + integrity sha512-/yh3oe26bZfCVGrIMRM9Z4hvvGJD+qx5tOLlydOkuBkm72aXON7D9+MucjJXTAcI8tF2Yq+JHa0478eHQOhnLg== + dependencies: + "@smithy/types" "^4.7.1" + fast-xml-parser "5.2.5" + tslib "^2.6.2" + +"@aws/lambda-invoke-store@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz#92d792a7dda250dfcb902e13228f37a81be57c8f" + integrity sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" @@ -804,7 +1182,7 @@ kuler "^2.0.0" "@drift-labs/sdk@file:./drift-common/protocol/sdk": - version "2.143.0-beta.3" + version "2.143.0-beta.4" dependencies: "@coral-xyz/anchor" "0.29.0" "@coral-xyz/anchor-30" "npm:@coral-xyz/anchor@0.30.1" @@ -836,7 +1214,7 @@ zstddec "0.1.0" "@drift/common@file:./drift-common/common-ts": - version "1.0.12" + version "1.0.13" dependencies: "@slack/web-api" "6.4.0" "@solana/spl-token" "0.3.8" @@ -2368,6 +2746,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/abort-controller@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.2.3.tgz#4615da3012b580ac3d1f0ee7b57ed7d7880bb29b" + integrity sha512-xWL9Mf8b7tIFuAlpjKtRPnHrR8XVrwTj5NPYO/QwZPtc0SDLsPxb56V5tzi5yspSMytISHybifez+4jlrx0vkQ== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/config-resolver@^4.1.4": version "4.1.4" resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.1.4.tgz#05d8eab8bb8eb73bec90c222fc19ac5608b1384e" @@ -2379,6 +2765,33 @@ "@smithy/util-middleware" "^4.0.4" tslib "^2.6.2" +"@smithy/config-resolver@^4.3.2", "@smithy/config-resolver@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.3.3.tgz#47de9dbb2b4ddf0d4d2dc9a56981f8273a5a7bea" + integrity sha512-xSql8A1Bl41O9JvGU/CtgiLBlwkvpHTSKRlvz9zOBvBCPjXghZ6ZkcVzmV2f7FLAA+80+aqKmIOmy8pEDrtCaw== + dependencies: + "@smithy/node-config-provider" "^4.3.3" + "@smithy/types" "^4.8.0" + "@smithy/util-config-provider" "^4.2.0" + "@smithy/util-middleware" "^4.2.3" + tslib "^2.6.2" + +"@smithy/core@^3.16.1", "@smithy/core@^3.17.0": + version "3.17.0" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.17.0.tgz#6acedc8ef21860a98143f655bac428af4049d189" + integrity sha512-Tir3DbfoTO97fEGUZjzGeoXgcQAUBRDTmuH9A8lxuP8ATrgezrAJ6cLuRvwdKN4ZbYNlHgKlBX69Hyu3THYhtg== + dependencies: + "@smithy/middleware-serde" "^4.2.3" + "@smithy/protocol-http" "^5.3.3" + "@smithy/types" "^4.8.0" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-middleware" "^4.2.3" + "@smithy/util-stream" "^4.5.3" + "@smithy/util-utf8" "^4.2.0" + "@smithy/uuid" "^1.1.0" + tslib "^2.6.2" + "@smithy/core@^3.7.0": version "3.7.0" resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.7.0.tgz#b3a98ccc48b1c408dfdb986aaa22d6affffd7795" @@ -2405,6 +2818,17 @@ "@smithy/url-parser" "^4.0.4" tslib "^2.6.2" +"@smithy/credential-provider-imds@^4.2.2", "@smithy/credential-provider-imds@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.3.tgz#b35d0d1f1b28f415e06282999eba2d53eb10a1c5" + integrity sha512-hA1MQ/WAHly4SYltJKitEsIDVsNmXcQfYBRv2e+q04fnqtAX5qXaybxy/fhUeAMCnQIdAjaGDb04fMHQefWRhw== + dependencies: + "@smithy/node-config-provider" "^4.3.3" + "@smithy/property-provider" "^4.2.3" + "@smithy/types" "^4.8.0" + "@smithy/url-parser" "^4.2.3" + tslib "^2.6.2" + "@smithy/eventstream-codec@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.0.4.tgz#35abc26d6829cc61a0d14950857ccc5320bf92d2" @@ -2461,6 +2885,17 @@ "@smithy/util-base64" "^4.0.0" tslib "^2.6.2" +"@smithy/fetch-http-handler@^5.3.3", "@smithy/fetch-http-handler@^5.3.4": + version "5.3.4" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.4.tgz#af6dd2f63550494c84ef029a5ceda81ef46965d3" + integrity sha512-bwigPylvivpRLCm+YK9I5wRIYjFESSVwl8JQ1vVx/XhCw0PtCi558NwTnT2DaVCl5pYlImGuQTSwMsZ+pIavRw== + dependencies: + "@smithy/protocol-http" "^5.3.3" + "@smithy/querystring-builder" "^4.2.3" + "@smithy/types" "^4.8.0" + "@smithy/util-base64" "^4.3.0" + tslib "^2.6.2" + "@smithy/hash-node@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.0.4.tgz#f867cfe6b702ed8893aacd3e097f8ca8ecba579e" @@ -2471,6 +2906,16 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/hash-node@^4.2.2": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.3.tgz#c85711fca84e022f05c71b921f98cb6a0f48e5ca" + integrity sha512-6+NOdZDbfuU6s1ISp3UOk5Rg953RJ2aBLNLLBEcamLjHAg1Po9Ha7QIB5ZWhdRUVuOUrT8BVFR+O2KIPmw027g== + dependencies: + "@smithy/types" "^4.8.0" + "@smithy/util-buffer-from" "^4.2.0" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/invalid-dependency@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz#8c2c539b2f22e857b4652bd2427a3d7a8befd610" @@ -2479,6 +2924,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/invalid-dependency@^4.2.2": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.3.tgz#4f126ddde90fe3d69d522fc37256ee853246c1ec" + integrity sha512-Cc9W5DwDuebXEDMpOpl4iERo8I0KFjTnomK2RMdhhR87GwrSmUmwMxS4P5JdRf+LsjOdIqumcerwRgYMr/tZ9Q== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/is-array-buffer@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" @@ -2493,6 +2946,13 @@ dependencies: tslib "^2.6.2" +"@smithy/is-array-buffer@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz#b0f874c43887d3ad44f472a0f3f961bcce0550c2" + integrity sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ== + dependencies: + tslib "^2.6.2" + "@smithy/middleware-content-length@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz#fad1f125779daf8d5f261dae6dbebba0f60c234b" @@ -2502,6 +2962,15 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/middleware-content-length@^4.2.2": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.3.tgz#b7d1d79ae674dad17e35e3518db4b1f0adc08964" + integrity sha512-/atXLsT88GwKtfp5Jr0Ks1CSa4+lB+IgRnkNrrYP0h1wL4swHNb0YONEvTceNKNdZGJsye+W2HH8W7olbcPUeA== + dependencies: + "@smithy/protocol-http" "^5.3.3" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/middleware-endpoint@^4.1.14", "@smithy/middleware-endpoint@^4.1.15": version "4.1.15" resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.15.tgz#31d93e6f33bbe2dbd120bda0833bce35cf960c39" @@ -2516,6 +2985,20 @@ "@smithy/util-middleware" "^4.0.4" tslib "^2.6.2" +"@smithy/middleware-endpoint@^4.3.3", "@smithy/middleware-endpoint@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.4.tgz#372d8818213f884e50e52b638fefe23b358cb812" + integrity sha512-/RJhpYkMOaUZoJEkddamGPPIYeKICKXOu/ojhn85dKDM0n5iDIhjvYAQLP3K5FPhgB203O3GpWzoK2OehEoIUw== + dependencies: + "@smithy/core" "^3.17.0" + "@smithy/middleware-serde" "^4.2.3" + "@smithy/node-config-provider" "^4.3.3" + "@smithy/shared-ini-file-loader" "^4.3.3" + "@smithy/types" "^4.8.0" + "@smithy/url-parser" "^4.2.3" + "@smithy/util-middleware" "^4.2.3" + tslib "^2.6.2" + "@smithy/middleware-retry@^4.1.15": version "4.1.16" resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.1.16.tgz#0e044d0da18e7019052ac61ec02a2956437376fd" @@ -2531,6 +3014,21 @@ tslib "^2.6.2" uuid "^9.0.1" +"@smithy/middleware-retry@^4.4.3": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.4.tgz#b732b04f67c9b60b3484267cd3aca317cc0ad300" + integrity sha512-vSgABQAkuUHRO03AhR2rWxVQ1un284lkBn+NFawzdahmzksAoOeVMnXXsuPViL4GlhRHXqFaMlc8Mj04OfQk1w== + dependencies: + "@smithy/node-config-provider" "^4.3.3" + "@smithy/protocol-http" "^5.3.3" + "@smithy/service-error-classification" "^4.2.3" + "@smithy/smithy-client" "^4.9.0" + "@smithy/types" "^4.8.0" + "@smithy/util-middleware" "^4.2.3" + "@smithy/util-retry" "^4.2.3" + "@smithy/uuid" "^1.1.0" + tslib "^2.6.2" + "@smithy/middleware-serde@^4.0.8": version "4.0.8" resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz#3704c8cc46acd0a7f910a78ee1d2f23ce928701f" @@ -2540,6 +3038,15 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/middleware-serde@^4.2.2", "@smithy/middleware-serde@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.3.tgz#a827e9c4ea9e51c79cca4d6741d582026a8b53eb" + integrity sha512-8g4NuUINpYccxiCXM5s1/V+uLtts8NcX4+sPEbvYQDZk4XoJfDpq5y2FQxfmUL89syoldpzNzA0R9nhzdtdKnQ== + dependencies: + "@smithy/protocol-http" "^5.3.3" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/middleware-stack@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz#58e0c6a0d7678c6ad4d6af8dd9a00f749ffac7c5" @@ -2548,6 +3055,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/middleware-stack@^4.2.2", "@smithy/middleware-stack@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.3.tgz#5a315aa9d0fd4faaa248780297c8cbacc31c2eba" + integrity sha512-iGuOJkH71faPNgOj/gWuEGS6xvQashpLwWB1HjHq1lNNiVfbiJLpZVbhddPuDbx9l4Cgl0vPLq5ltRfSaHfspA== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/node-config-provider@^4.1.3": version "4.1.3" resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz#6626fe26c6fe7b0df34f71cb72764ccba414a815" @@ -2558,6 +3073,16 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/node-config-provider@^4.3.2", "@smithy/node-config-provider@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.3.tgz#44140a1e6bc666bcf16faf68c35d3dae4ba8cad5" + integrity sha512-NzI1eBpBSViOav8NVy1fqOlSfkLgkUjUTlohUSgAEhHaFWA3XJiLditvavIP7OpvTjDp5u2LhtlBhkBlEisMwA== + dependencies: + "@smithy/property-provider" "^4.2.3" + "@smithy/shared-ini-file-loader" "^4.3.3" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/node-http-handler@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.1.0.tgz#6b528cd0da0c35755b34afba207b7db972b0eb92" @@ -2569,6 +3094,17 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/node-http-handler@^4.4.1", "@smithy/node-http-handler@^4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.2.tgz#0620e07d3758d743673c62bf0519b28ddb6e71f6" + integrity sha512-MHFvTjts24cjGo1byXqhXrbqm7uznFD/ESFx8npHMWTFQVdBZjrT1hKottmp69LBTRm/JQzP/sn1vPt0/r6AYQ== + dependencies: + "@smithy/abort-controller" "^4.2.3" + "@smithy/protocol-http" "^5.3.3" + "@smithy/querystring-builder" "^4.2.3" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/property-provider@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.0.4.tgz#303a8fd99665fff61eeb6ec3922eee53838962c5" @@ -2577,6 +3113,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/property-provider@^4.2.2", "@smithy/property-provider@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.3.tgz#a6c82ca0aa1c57f697464bee496f3fec58660864" + integrity sha512-+1EZ+Y+njiefCohjlhyOcy1UNYjT+1PwGFHCxA/gYctjg3DQWAU19WigOXAco/Ql8hZokNehpzLd0/+3uCreqQ== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/protocol-http@^5.1.2": version "5.1.2" resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.1.2.tgz#8094860c2407f250b80c95899e0385112d6eb98b" @@ -2585,6 +3129,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/protocol-http@^5.3.2", "@smithy/protocol-http@^5.3.3": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.3.tgz#55b35c18bdc0f6d86e78f63961e50ba4ff1c5d73" + integrity sha512-Mn7f/1aN2/jecywDcRDvWWWJF4uwg/A0XjFMJtj72DsgHTByfjRltSqcT9NyE9RTdBSN6X1RSXrhn/YWQl8xlw== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/querystring-builder@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz#f7546efd59d457b3d2525a330c6137e5f907864c" @@ -2594,6 +3146,15 @@ "@smithy/util-uri-escape" "^4.0.0" tslib "^2.6.2" +"@smithy/querystring-builder@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.3.tgz#ca273ae8c21fce01a52632202679c0f9e2acf41a" + integrity sha512-LOVCGCmwMahYUM/P0YnU/AlDQFjcu+gWbFJooC417QRB/lDJlWSn8qmPSDp+s4YVAHOgtgbNG4sR+SxF/VOcJQ== + dependencies: + "@smithy/types" "^4.8.0" + "@smithy/util-uri-escape" "^4.2.0" + tslib "^2.6.2" + "@smithy/querystring-parser@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz#307ab95ee5f1a142ab46c2eddebeae68cb2f703d" @@ -2602,6 +3163,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/querystring-parser@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.3.tgz#b6d7d5cd300b4083c62d9bd30915f782d01f503e" + integrity sha512-cYlSNHcTAX/wc1rpblli3aUlLMGgKZ/Oqn8hhjFASXMCXjIqeuQBei0cnq2JR8t4RtU9FpG6uyl6PxyArTiwKA== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/service-error-classification@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-1.1.0.tgz#264dd432ae513b3f2ad9fc6f461deda8c516173c" @@ -2614,6 +3183,13 @@ dependencies: "@smithy/types" "^4.3.1" +"@smithy/service-error-classification@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.3.tgz#ecb41dd514841eebb93e91012ae5e343040f6828" + integrity sha512-NkxsAxFWwsPsQiwFG2MzJ/T7uIR6AQNh1SzcxSUnmmIqIQMlLRQDKhc17M7IYjiuBXhrQRjQTo3CxX+DobS93g== + dependencies: + "@smithy/types" "^4.8.0" + "@smithy/shared-ini-file-loader@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz#33c63468b95cfd5e7d642c8131d7acc034025e00" @@ -2622,6 +3198,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/shared-ini-file-loader@^4.3.2", "@smithy/shared-ini-file-loader@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.3.tgz#1d5162cd3a14f57e4fde56f65aa188e8138c1248" + integrity sha512-9f9Ixej0hFhroOK2TxZfUUDR13WVa8tQzhSzPDgXe5jGL3KmaM9s8XN7RQwqtEypI82q9KHnKS71CJ+q/1xLtQ== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/signature-v4@^5.1.2": version "5.1.2" resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.1.2.tgz#5afd9d428bd26bb660bee8075b6e89fe93600c22" @@ -2636,6 +3220,20 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/signature-v4@^5.3.2": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.3.tgz#5ff13cfaa29cb531061c2582cb599b39e040e52e" + integrity sha512-CmSlUy+eEYbIEYN5N3vvQTRfqt0lJlQkaQUIf+oizu7BbDut0pozfDjBGecfcfWf7c62Yis4JIEgqQ/TCfodaA== + dependencies: + "@smithy/is-array-buffer" "^4.2.0" + "@smithy/protocol-http" "^5.3.3" + "@smithy/types" "^4.8.0" + "@smithy/util-hex-encoding" "^4.2.0" + "@smithy/util-middleware" "^4.2.3" + "@smithy/util-uri-escape" "^4.2.0" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/smithy-client@^4.4.6", "@smithy/smithy-client@^4.4.7": version "4.4.7" resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.4.7.tgz#68e9f7785179060896ed51d52cd4f4cfc1cbffb5" @@ -2649,6 +3247,19 @@ "@smithy/util-stream" "^4.2.3" tslib "^2.6.2" +"@smithy/smithy-client@^4.8.1", "@smithy/smithy-client@^4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.9.0.tgz#d72f2ea7ad53db113f1b0f6b13ccddc19aae9aba" + integrity sha512-qz7RTd15GGdwJ3ZCeBKLDQuUQ88m+skh2hJwcpPm1VqLeKzgZvXf6SrNbxvx7uOqvvkjCMXqx3YB5PDJyk00ww== + dependencies: + "@smithy/core" "^3.17.0" + "@smithy/middleware-endpoint" "^4.3.4" + "@smithy/middleware-stack" "^4.2.3" + "@smithy/protocol-http" "^5.3.3" + "@smithy/types" "^4.8.0" + "@smithy/util-stream" "^4.5.3" + tslib "^2.6.2" + "@smithy/types@^4.3.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.3.1.tgz#c11276ea16235d798f47a68aef9f44d3dbb70dd4" @@ -2656,6 +3267,13 @@ dependencies: tslib "^2.6.2" +"@smithy/types@^4.7.1", "@smithy/types@^4.8.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.8.0.tgz#e6f65e712478910b74747081e6046e68159f767d" + integrity sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ== + dependencies: + tslib "^2.6.2" + "@smithy/url-parser@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.0.4.tgz#049143f4c156356e177bd69242675db26fe4f4db" @@ -2665,6 +3283,15 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/url-parser@^4.2.2", "@smithy/url-parser@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.3.tgz#82508f273a3f074d47d0919f7ce08028c6575c2f" + integrity sha512-I066AigYvY3d9VlU3zG9XzZg1yT10aNqvCaBTw9EPgu5GrsEl1aUkcMvhkIXascYH1A8W0LQo3B1Kr1cJNcQEw== + dependencies: + "@smithy/querystring-parser" "^4.2.3" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/util-base64@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.0.0.tgz#8345f1b837e5f636e5f8470c4d1706ae0c6d0358" @@ -2674,6 +3301,15 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/util-base64@^4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.3.0.tgz#5e287b528793aa7363877c1a02cd880d2e76241d" + integrity sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ== + dependencies: + "@smithy/util-buffer-from" "^4.2.0" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/util-body-length-browser@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz#965d19109a4b1e5fe7a43f813522cce718036ded" @@ -2681,6 +3317,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-body-length-browser@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz#04e9fc51ee7a3e7f648a4b4bcdf96c350cfa4d61" + integrity sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg== + dependencies: + tslib "^2.6.2" + "@smithy/util-body-length-node@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz#3db245f6844a9b1e218e30c93305bfe2ffa473b3" @@ -2688,6 +3331,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-body-length-node@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz#79c8a5d18e010cce6c42d5cbaf6c1958523e6fec" + integrity sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA== + dependencies: + tslib "^2.6.2" + "@smithy/util-buffer-from@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" @@ -2704,6 +3354,14 @@ "@smithy/is-array-buffer" "^4.0.0" tslib "^2.6.2" +"@smithy/util-buffer-from@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz#7abd12c4991b546e7cee24d1e8b4bfaa35c68a9d" + integrity sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew== + dependencies: + "@smithy/is-array-buffer" "^4.2.0" + tslib "^2.6.2" + "@smithy/util-config-provider@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz#e0c7c8124c7fba0b696f78f0bd0ccb060997d45e" @@ -2711,6 +3369,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-config-provider@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz#2e4722937f8feda4dcb09672c59925a4e6286cfc" + integrity sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q== + dependencies: + tslib "^2.6.2" + "@smithy/util-defaults-mode-browser@^4.0.22": version "4.0.23" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.23.tgz#74887922f87da4f4acbf523b5c0271f11af5997d" @@ -2722,6 +3387,16 @@ bowser "^2.11.0" tslib "^2.6.2" +"@smithy/util-defaults-mode-browser@^4.3.2": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.3.tgz#e858d6a1ee7de77a166c1ae71d2676df623c4ee7" + integrity sha512-vqHoybAuZXbFXZqgzquiUXtdY+UT/aU33sxa4GBPkiYklmR20LlCn+d3Wc3yA5ZM13gQ92SZe/D8xh6hkjx+IQ== + dependencies: + "@smithy/property-provider" "^4.2.3" + "@smithy/smithy-client" "^4.9.0" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/util-defaults-mode-node@^4.0.22": version "4.0.23" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.23.tgz#dd22609289183fe3d722f9a1601b6cc618f9efa7" @@ -2735,6 +3410,19 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/util-defaults-mode-node@^4.2.3": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.4.tgz#c85d379f37917afbd4777449de26cd1858b1bbb0" + integrity sha512-X5/xrPHedifo7hJUUWKlpxVb2oDOiqPUXlvsZv1EZSjILoutLiJyWva3coBpn00e/gPSpH8Rn2eIbgdwHQdW7Q== + dependencies: + "@smithy/config-resolver" "^4.3.3" + "@smithy/credential-provider-imds" "^4.2.3" + "@smithy/node-config-provider" "^4.3.3" + "@smithy/property-provider" "^4.2.3" + "@smithy/smithy-client" "^4.9.0" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/util-endpoints@^3.0.6": version "3.0.6" resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz#a24b0801a1b94c0de26ad83da206b9add68117f2" @@ -2744,6 +3432,15 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/util-endpoints@^3.2.2": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.3.tgz#8bbb80f1ad5769d9f73992c5979eea3b74d7baa9" + integrity sha512-aCfxUOVv0CzBIkU10TubdgKSx5uRvzH064kaiPEWfNIvKOtNpu642P4FP1hgOFkjQIkDObrfIDnKMKkeyrejvQ== + dependencies: + "@smithy/node-config-provider" "^4.3.3" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/util-hex-encoding@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz#dd449a6452cffb37c5b1807ec2525bb4be551e8d" @@ -2751,6 +3448,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-hex-encoding@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz#1c22ea3d1e2c3a81ff81c0a4f9c056a175068a7b" + integrity sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw== + dependencies: + tslib "^2.6.2" + "@smithy/util-middleware@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.0.4.tgz#8f639de049082c687841ea5e69c6c36e12e31a3c" @@ -2759,6 +3463,14 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/util-middleware@^4.2.2", "@smithy/util-middleware@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.2.3.tgz#7c73416a6e3d3207a2d34a1eadd9f2b6a9811bd6" + integrity sha512-v5ObKlSe8PWUHCqEiX2fy1gNv6goiw6E5I/PN2aXg3Fb/hse0xeaAnSpXDiWl7x6LamVKq7senB+m5LOYHUAHw== + dependencies: + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/util-retry@^1.0.3": version "1.1.0" resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-1.1.0.tgz#f6e62ec7d7d30f1dd9608991730ba7a86e445047" @@ -2776,6 +3488,15 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/util-retry@^4.2.2", "@smithy/util-retry@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.3.tgz#b1e5c96d96aaf971b68323ff8ba8754f914f22a0" + integrity sha512-lLPWnakjC0q9z+OtiXk+9RPQiYPNAovt2IXD3CP4LkOnd9NpUsxOjMx1SnoUVB7Orb7fZp67cQMtTBKMFDvOGg== + dependencies: + "@smithy/service-error-classification" "^4.2.3" + "@smithy/types" "^4.8.0" + tslib "^2.6.2" + "@smithy/util-stream@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.2.3.tgz#7980fb94dbee96301b0b2610de8ae1700c7daab1" @@ -2790,6 +3511,20 @@ "@smithy/util-utf8" "^4.0.0" tslib "^2.6.2" +"@smithy/util-stream@^4.5.2", "@smithy/util-stream@^4.5.3": + version "4.5.3" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.3.tgz#3ddfbf70f282e371bf45dc0bdc21cb39b04b233f" + integrity sha512-oZvn8a5bwwQBNYHT2eNo0EU8Kkby3jeIg1P2Lu9EQtqDxki1LIjGRJM6dJ5CZUig8QmLxWxqOKWvg3mVoOBs5A== + dependencies: + "@smithy/fetch-http-handler" "^5.3.4" + "@smithy/node-http-handler" "^4.4.2" + "@smithy/types" "^4.8.0" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-buffer-from" "^4.2.0" + "@smithy/util-hex-encoding" "^4.2.0" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/util-uri-escape@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz#a96c160c76f3552458a44d8081fade519d214737" @@ -2797,6 +3532,13 @@ dependencies: tslib "^2.6.2" +"@smithy/util-uri-escape@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz#096a4cec537d108ac24a68a9c60bee73fc7e3a9e" + integrity sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA== + dependencies: + tslib "^2.6.2" + "@smithy/util-utf8@^2.0.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" @@ -2813,6 +3555,14 @@ "@smithy/util-buffer-from" "^4.0.0" tslib "^2.6.2" +"@smithy/util-utf8@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.2.0.tgz#8b19d1514f621c44a3a68151f3d43e51087fed9d" + integrity sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw== + dependencies: + "@smithy/util-buffer-from" "^4.2.0" + tslib "^2.6.2" + "@smithy/util-waiter@^4.0.6": version "4.0.6" resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.0.6.tgz#38044da5053f0d9118df05f55cd8fbec14ecf9da" @@ -2822,6 +3572,13 @@ "@smithy/types" "^4.3.1" tslib "^2.6.2" +"@smithy/uuid@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@smithy/uuid/-/uuid-1.1.0.tgz#9fd09d3f91375eab94f478858123387df1cda987" + integrity sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw== + dependencies: + tslib "^2.6.2" + "@solana-program/address-lookup-table@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@solana-program/address-lookup-table/-/address-lookup-table-0.7.0.tgz#6651657308547fefb59ce2e20b1871270e93d48d" From 3d4cf24296e059510e46eb4adf40de87f7d62ea8 Mon Sep 17 00:00:00 2001 From: wphan Date: Tue, 21 Oct 2025 16:33:30 -0700 Subject: [PATCH 3/4] add auction anchoring in v2 --- src/athena/README.md | 348 ++++++++++++++++++ src/athena/client.ts | 44 ++- src/athena/index.ts | 1 - .../repositories/fillQualityAnalytics.ts | 24 +- src/athena/utils.ts | 1 - src/index.ts | 51 ++- src/publishers/dlobPublisher.ts | 229 +++++++++++- src/utils/utils.ts | 154 +++++++- 8 files changed, 826 insertions(+), 26 deletions(-) create mode 100644 src/athena/README.md diff --git a/src/athena/README.md b/src/athena/README.md new file mode 100644 index 0000000..ebe9a2f --- /dev/null +++ b/src/athena/README.md @@ -0,0 +1,348 @@ +# Athena Integration + +This module provides connectivity to AWS Athena for querying historical Drift protocol data. + +## Setup + +### Prerequisites + +1. AWS credentials configured with access to Athena +2. Environment variables: + - `AWS_REGION` (optional, defaults to `us-east-1`) + - `AWS_ACCESS_KEY_ID` (required) + - `AWS_SECRET_ACCESS_KEY` (required) + - `ATHENA_DATABASE` (optional, defaults to `staging-archive`) + - `ATHENA_OUTPUT_BUCKET` (optional, defaults to `staging-data-ingestion-bucket`) + +### Installation + +The required AWS SDK packages are already included in `package.json`: +```bash +yarn install +``` + +## Usage + +### Basic Query Example + +```typescript +import { Athena } from './athena/client'; + +const { query } = Athena(); + +const results = await query(` + SELECT * FROM eventtype_traderecord + WHERE markettype = 'perp' + LIMIT 10 +`); + +console.log(results); +``` + +### Using the Fill Quality Analytics Repository + +```typescript +import { FillQualityAnalyticsRepository } from './athena/repositories/fillQualityAnalytics'; + +const repository = FillQualityAnalyticsRepository(); + +// Get taker fill vs oracle basis points for ALL perp markets +// Time range: last 7 days +const nowMs = Date.now(); +const weekAgoMs = nowMs - 7 * 24 * 60 * 60 * 1000; + +const results = await repository.getTakerFillVsOracleBps( + weekAgoMs, // from (unix timestamp in milliseconds) + nowMs, // to (unix timestamp in milliseconds) + 9, // baseDecimals (default: 9) + 60 // smoothingMinutes (default: 60) +); + +// Results contain time series data with columns: +// - Time: timestamp +// - MarketIndex: the perp market index +// - TakerBuyBpsFromOracle_1e0: average for buy orders < $1000 +// - TakerBuyBpsFromOracle_1e3: average for buy orders $1k-$10k +// - TakerBuyBpsFromOracle_1e4: average for buy orders $10k-$100k +// - TakerBuyBpsFromOracle_1e5: average for buy orders $100k-$1M +// - TakerBuyBpsFromOracle_1e6: average for buy orders > $1M +// - (Similar columns for sell orders) +// - Zero: baseline (always 0) + +// Data is partitioned by MarketIndex, so you can filter/group by market +const solPerpData = results.filter(r => r.MarketIndex === '0'); +console.log(solPerpData); +``` + +### Batch Queries + +```typescript +import { Athena } from './athena/client'; + +const { batchQuery } = Athena(); + +const { results, errors } = await batchQuery({ + queries: [ + { query: 'SELECT COUNT(*) FROM eventtype_traderecord' }, + { query: 'SELECT COUNT(*) FROM eventtype_orderrecord' }, + ] +}); + +console.log('Results:', results); +console.log('Errors:', errors); +``` + +### Custom Database/Bucket + +```typescript +import { Athena } from './athena/client'; + +const { query } = Athena({ + overrideDatabaseName: 'my_custom_db', + overrideBucketName: 'my-custom-bucket' +}); +``` + +## Architecture + +### Client (`client.ts`) +- Handles Athena query execution +- Manages query lifecycle (start, poll, retrieve results) +- Implements rate limiting via Bottleneck +- Provides batch query support + +### Utilities (`utils.ts`) +- `getTimePartition`: Generate time partition filters for queries +- `getTimeRangeAndPartitions`: Generate time range and partition filters + +### Repositories +- **Fill Quality Analytics** (`repositories/fillQualityAnalytics.ts`): Analyzes taker fill quality vs oracle prices, bucketed by order size cohorts + +## Query Best Practices + +1. **Always use partition filters** to minimize data scanned: + ```sql + WHERE year = '2024' AND month = '01' AND day = '15' + ``` + +2. **Use the utility functions** for generating time-based filters: + ```typescript + import { getTimeRangeAndPartitions } from './athena/utils'; + + const query = ` + ${getTimeRangeAndPartitions(from, to)} + SELECT * FROM eventtype_traderecord + JOIN valid_partitions vp ON ... + `; + ``` + +3. **Monitor costs**: Each query logs bytes scanned. Athena charges per TB scanned. + +4. **Use batch queries** when running multiple independent queries to improve throughput. + +## Error Handling + +The client automatically retries failed queries up to 3 times with exponential backoff. Queries timeout after 5 minutes (300s) by default. + +```typescript +try { + const results = await query(queryString); +} catch (error) { + console.error('Query failed:', error.message); + // Handle error appropriately +} +``` + +## Adding New Repositories + +To add a new analytics repository: + +1. Create a new file in `repositories/` (e.g., `myAnalytics.ts`) +2. Define TypeScript interfaces for your results +3. Create a repository function that uses the Athena client +4. Export from `index.ts` + +Example: + +```typescript +// repositories/myAnalytics.ts +import { Athena } from '../client'; + +export interface MyResult { + field1: string; + field2: string; +} + +export const MyAnalyticsRepository = () => { + const { query } = Athena(); + + const getMyData = async (from: number, to: number): Promise => { + const queryString = ` + SELECT field1, field2 + FROM my_table + WHERE ts BETWEEN ${from} AND ${to} + `; + + const results = await query(queryString); + + return results.map((r) => ({ + field1: r.field1 || '', + field2: r.field2 || '', + })); + }; + + return { getMyData }; +}; +``` + +```typescript +// index.ts +export * from './repositories/myAnalytics'; +``` + +## Integration with DLOBPublisher + +The DLOBPublisher (`src/publishers/dlobPublisher.ts`) includes automatic fetching and caching of fill quality analytics data to Redis. + +### Configuration + +Enable fill quality analytics via environment variables: + +```bash +# Enable the feature (default: false) +ENABLE_FILL_QUALITY_ANALYTICS=true + +# How often to fetch and update analytics (default: 300000 = 5 minutes) +FILL_QUALITY_ANALYTICS_INTERVAL=300000 + +# Historical data lookback window (default: 86400000 = 24 hours) +FILL_QUALITY_ANALYTICS_LOOKBACK_MS=86400000 + +# Rolling average smoothing window in minutes (default: 60) +FILL_QUALITY_ANALYTICS_SMOOTHING_MINUTES=60 +``` + +### How it Works + +1. **On Startup**: Immediately fetches fill quality analytics for all perp markets +2. **Periodic Updates**: Re-fetches data every `FILL_QUALITY_ANALYTICS_INTERVAL` milliseconds +3. **Redis Storage**: Stores results in Redis with the following keys: + - `taker_fill_vs_oracle_bps:market:{marketIndex}` - Individual market data + - `taker_fill_vs_oracle_bps:summary` - Summary with metadata + +### Redis Data Format + +Per-market keys contain: +```json +{ + "time": "2024-01-15 10:30:00.000", + "marketIndex": "0", + "takerBuyBpsFromOracle": { + "all": "5.23", + "1e0": "8.45", + "1e3": "6.12", + "1e4": "4.89", + "1e5": "3.56", + "1e6": "2.34" + }, + "takerSellBpsFromOracle": { + "all": "-4.87", + "1e0": "-7.23", + "1e3": "-5.45", + "1e4": "-4.12", + "1e5": "-3.01", + "1e6": "-2.15" + }, + "updatedAt": "2024-01-15T10:35:00.000Z" +} +``` + +Summary key contains: +```json +{ + "markets": ["0", "1", "2"], + "lastUpdated": "2024-01-15T10:35:00.000Z", + "lookbackMs": 86400000, + "smoothingMinutes": 60 +} +``` + +### Accessing the Data + +Downstream services can read the cached analytics from Redis without querying Athena directly: + +```typescript +import { RedisClient } from '@drift/common/clients'; + +const redis = new RedisClient(); +await redis.connect(); + +// Get summary +const summary = await redis.get('taker_fill_vs_oracle_bps:summary'); +console.log(JSON.parse(summary)); + +// Get data for SOL-PERP (market index 0) +const solData = await redis.get('taker_fill_vs_oracle_bps:market:0'); +console.log(JSON.parse(solData)); +``` + +This architecture allows multiple services to access expensive analytics queries without hitting Athena repeatedly. + +## Testing + +### Test Redis Polling + +To verify that fill quality analytics data is being written to and updated in Redis: + +```bash +# Start the DLOBPublisher with fill quality analytics enabled +ENABLE_FILL_QUALITY_ANALYTICS=true yarn dlob-publish + +# In another terminal, run the Redis polling test +yarn test:fill-quality-redis +``` + +The polling test script will: +- Connect to Redis +- Poll every 10 seconds for markets 0, 1, 2 (SOL, BTC, ETH) +- Display the summary and per-market data +- Track when data is updated (shows 🆕 NEW UPDATE when timestamps change) +- Validate data structure integrity +- Show data age and warn if stale (>10 minutes old) + +**Example output:** +``` +========== Polling iteration 1 ========== + +📋 Summary: + Markets available: 0, 1, 2 + Last updated: 2024-01-15T10:35:00.000Z + Lookback: 86400000ms (24h) + Smoothing: 60 minutes + +📊 Market Data: + 🆕 NEW UPDATE - SOL-PERP (Market 0) + Data timestamp: 2024-01-15 10:30:00.000 + Updated at: 2024-01-15T10:35:00.000Z + Age: 15s ago + Taker Buy (all): 5.23 bps + Taker Sell (all): -4.87 bps + Large orders (>$1M): Buy=2.34bps, Sell=-2.15bps + + 🆕 NEW UPDATE - BTC-PERP (Market 1) + Data timestamp: 2024-01-15 10:30:00.000 + Updated at: 2024-01-15T10:35:00.000Z + Age: 15s ago + Taker Buy (all): 4.12 bps + Taker Sell (all): -3.89 bps + Large orders (>$1M): Buy=1.89bps, Sell=-1.76bps + +📈 Update Tracking: + ✅ All 3 markets have data + +🔍 Validation: + ✅ All data structures are valid +``` + +The script will continue polling until you press `Ctrl+C`. + diff --git a/src/athena/client.ts b/src/athena/client.ts index c0494f7..2cd1de2 100644 --- a/src/athena/client.ts +++ b/src/athena/client.ts @@ -42,16 +42,24 @@ const parseRow = (row: Row, columnInfo: ColumnInfo[]): QueryResult => { const athena = new AthenaClient({ region: process.env.AWS_REGION, - retryStrategy: new ConfiguredRetryStrategy(3, (attempt: number) => 100 + attempt * 1000), + retryStrategy: new ConfiguredRetryStrategy( + 3, + (attempt: number) => 100 + attempt * 1000 + ), }); export const Athena = ({ overrideDatabaseName, overrideBucketName, }: { overrideDatabaseName?: string; overrideBucketName?: string } = {}) => { - const database = overrideDatabaseName ?? process.env.ATHENA_DATABASE ?? DEFAULT_ATHENA_DATABASE; + const database = + overrideDatabaseName ?? + process.env.ATHENA_DATABASE ?? + DEFAULT_ATHENA_DATABASE; const outputLocation = `s3://${ - overrideBucketName ?? process.env.ATHENA_OUTPUT_BUCKET ?? DEFAULT_ATHENA_OUTPUT_BUCKET + overrideBucketName ?? + process.env.ATHENA_OUTPUT_BUCKET ?? + DEFAULT_ATHENA_OUTPUT_BUCKET }/athena`; const startQuery = async (query: string, params?: Record) => { @@ -104,7 +112,9 @@ export const Athena = ({ while (Date.now() - startTime < timeout) { const execution = await getQueryExecution(queryExecutionId); logger.info( - `Total bytes scanned: ${(execution?.Statistics?.DataScannedInBytes ?? 0) / 1024}kb` + `Total bytes scanned: ${ + (execution?.Statistics?.DataScannedInBytes ?? 0) / 1024 + }kb` ); const state = execution?.Status?.State; @@ -112,7 +122,9 @@ export const Athena = ({ case QueryExecutionState.SUCCEEDED: return; case QueryExecutionState.FAILED: - throw new Error(`Query failed: ${execution?.Status?.StateChangeReason}`); + throw new Error( + `Query failed: ${execution?.Status?.StateChangeReason}` + ); case QueryExecutionState.CANCELLED: throw new Error('Query was cancelled'); default: @@ -135,15 +147,22 @@ export const Athena = ({ ); }; - const getAllQueryResults = async (queryExecutionId: string): Promise => { + const getAllQueryResults = async ( + queryExecutionId: string + ): Promise => { let nextToken: string | undefined; const allResults: QueryResult[] = []; do { const results = await getQueryResults(queryExecutionId, nextToken); - if (results.ResultSet?.Rows && results.ResultSet.ResultSetMetadata?.ColumnInfo) { + if ( + results.ResultSet?.Rows && + results.ResultSet.ResultSetMetadata?.ColumnInfo + ) { const columnInfo = results.ResultSet.ResultSetMetadata.ColumnInfo; - const rows = nextToken ? results.ResultSet.Rows : results.ResultSet.Rows.slice(1); + const rows = nextToken + ? results.ResultSet.Rows + : results.ResultSet.Rows.slice(1); const parsedRows = rows.map((row) => parseRow(row, columnInfo)); allResults.push(...parsedRows); } @@ -183,10 +202,14 @@ export const Athena = ({ await Promise.all( queries.map(async ({ query: queryString, params }, index) => { try { - const result = await limiter.schedule(() => query(queryString, params)); + const result = await limiter.schedule(() => + query(queryString, params) + ); results[index] = result; } catch (error) { - logger.error(`Error in batch query ${index}: ${queryString}, Error: ${error}`); + logger.error( + `Error in batch query ${index}: ${queryString}, Error: ${error}` + ); errors[index] = error as Error; } }) @@ -212,4 +235,3 @@ export const Athena = ({ waitForQueryCompletion, }; }; - diff --git a/src/athena/index.ts b/src/athena/index.ts index e9fb402..ae965fc 100644 --- a/src/athena/index.ts +++ b/src/athena/index.ts @@ -1,4 +1,3 @@ export * from './client'; export * from './utils'; export * from './repositories/fillQualityAnalytics'; - diff --git a/src/athena/repositories/fillQualityAnalytics.ts b/src/athena/repositories/fillQualityAnalytics.ts index 03c75ab..291d241 100644 --- a/src/athena/repositories/fillQualityAnalytics.ts +++ b/src/athena/repositories/fillQualityAnalytics.ts @@ -1,7 +1,6 @@ import { Athena } from '../client'; export interface TakerFillVsOracleBpsResult { - Time: string; MarketIndex: string; TakerBuyBpsFromOracle_ALL: string | null; TakerSellBpsFromOracle_ALL: string | null; @@ -18,6 +17,27 @@ export interface TakerFillVsOracleBpsResult { Zero: string; } +export type TakerFillVsOracleBpsRedisResult = { + marketIndex: string; + takerBuyBpsFromOracle: { + all: string | null; + '1e0': string | null; + '1e3': string | null; + '1e4': string | null; + '1e5': string | null; + '1e6': string | null; + }; + takerSellBpsFromOracle: { + all: string | null; + '1e0': string | null; + '1e3': string | null; + '1e4': string | null; + '1e5': string | null; + '1e6': string | null; + }; + updatedAtTs: number; +}; + export const FillQualityAnalyticsRepository = () => { const { query } = Athena(); @@ -255,7 +275,6 @@ ORDER BY MarketIndex; const results = await query(queryString); return results.map((result) => ({ - Time: result.Time || '', MarketIndex: result.MarketIndex || '', TakerBuyBpsFromOracle_ALL: result.TakerBuyBpsFromOracle_ALL, TakerSellBpsFromOracle_ALL: result.TakerSellBpsFromOracle_ALL, @@ -277,4 +296,3 @@ ORDER BY MarketIndex; getTakerFillVsOracleBps, }; }; - diff --git a/src/athena/utils.ts b/src/athena/utils.ts index 484ce10..4fd9ec9 100644 --- a/src/athena/utils.ts +++ b/src/athena/utils.ts @@ -37,4 +37,3 @@ export const getTimeRangeAndPartitions = ( ) AS ${valid_partitions_label}(year, month, day) )`; }; - diff --git a/src/index.ts b/src/index.ts index 9651ee5..f3ae323 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,8 +50,9 @@ import FEATURE_FLAGS from './utils/featureFlags'; import { getDLOBProviderFromOrderSubscriber } from './dlobProvider'; import { setGlobalDispatcher, Agent } from 'undici'; import { HermesClient } from '@pythnetwork/hermes-client'; -import { COMMON_UI_UTILS } from '@drift/common'; +import { COMMON_UI_UTILS, ENUM_UTILS } from '@drift/common'; import { AuctionParamArgs } from './utils/types'; +import { TakerFillVsOracleBpsRedisResult } from './athena/repositories/fillQualityAnalytics'; setGlobalDispatcher( new Agent({ @@ -947,6 +948,7 @@ const main = async (): Promise => { forceUpToSlippage, maxLeverageSelected, maxLeverageOrderSize, + version, } = req.query; // Validate required parameters @@ -959,6 +961,31 @@ const main = async (): Promise => { return; } + const apiVersion = version ? parseInt(version as string) : 1; + + let redisFillQualityInfo: TakerFillVsOracleBpsRedisResult | undefined; + if (apiVersion === 2) { + const redisKey = `taker_fill_vs_oracle_bps:market:${marketIndex}`; + try { + const redisValue = await fetchFromRedis( + redisKey, + (responses) => responses[0] as any + ); + if (redisValue) { + const parsed = JSON.parse(redisValue); + redisFillQualityInfo = + typeof parsed === 'string' ? JSON.parse(parsed) : parsed; + } + // Fall through to existing logic below + } catch (err) { + logger.error( + `Version 2: Error fetching redis stats for market ${marketIndex}:`, + err + ); + // Fall through to existing logic below + } + } + // Parse and validate values const parsedMarketIndex = parseInt(marketIndex as string); if (isNaN(parsedMarketIndex)) { @@ -1029,7 +1056,9 @@ const main = async (): Promise => { inputParams, driftClient, fetchFromRedis, - selectMostRecentBySlot + selectMostRecentBySlot, + redisFillQualityInfo, + apiVersion ); if (!result.success) { @@ -1043,6 +1072,24 @@ const main = async (): Promise => { result.data.marketOrderParams ); + // Log final auction prices for debugging + logger.info( + JSON.stringify({ + event: 'auction_params_derived', + marketIndex: parsedMarketIndex, + direction: direction, + apiVersion, + finalAuctionParams: { + auctionStartPrice: auctionParams.auctionStartPrice?.toString(), + auctionEndPrice: auctionParams.auctionEndPrice?.toString(), + price: auctionParams.price?.toString(), + oraclePriceOffset: auctionParams.oraclePriceOffset?.toString(), + auctionDuration: auctionParams.auctionDuration?.toString(), + orderType: ENUM_UTILS.toStr(auctionParams.orderType), + }, + }) + ); + const response = { data: { params: formatAuctionParamsForResponse(auctionParams), diff --git a/src/publishers/dlobPublisher.ts b/src/publishers/dlobPublisher.ts index adf7b9c..cf91ae0 100644 --- a/src/publishers/dlobPublisher.ts +++ b/src/publishers/dlobPublisher.ts @@ -43,6 +43,10 @@ import { handleHealthCheck } from '../core/middleware'; import { setGlobalDispatcher, Agent } from 'undici'; import { Metrics } from '../core/metricsV2'; import { OrderSubscriberFiltered } from '../dlob-subscriber/OrderSubscriberFiltered'; +import { + FillQualityAnalyticsRepository, + TakerFillVsOracleBpsRedisResult, +} from '../athena/repositories/fillQualityAnalytics'; setGlobalDispatcher( new Agent({ @@ -93,6 +97,10 @@ const tobStuckGauge = metricsV2.addGauge( 'tob_stuck_duration', 'Duration TOB has been stuck for each market' ); +const takerFillBpsFromOracleGauge = metricsV2.addGauge( + 'taker_fill_bps_from_oracle', + 'Taker fill BPS from oracle by market, side, and cohort' +); metricsV2.finalizeObservables(); //@ts-ignore @@ -109,7 +117,7 @@ const endpoint = process.env.ENDPOINT; const grpcEndpoint = useGrpc ? process.env.GRPC_ENDPOINT ?? endpoint + `/${token}` : ''; -const grpcClient = process.env.GRPC_CLIENT ?? 'yellowstone' +const grpcClient = process.env.GRPC_CLIENT ?? 'yellowstone'; const wsEndpoint = process.env.WS_ENDPOINT; const useOrderSubscriber = @@ -122,6 +130,16 @@ const WS_FALLBACK_FETCH_INTERVAL = 60_000; const KILLSWITCH_SLOT_DIFF_THRESHOLD = parseInt(process.env.KILLSWITCH_SLOT_DIFF_THRESHOLD) || 200; +// Fill Quality Analytics configuration +const ENABLE_FILL_QUALITY_ANALYTICS = + process.env.ENABLE_FILL_QUALITY_ANALYTICS?.toLowerCase() === 'true'; +const FILL_QUALITY_ANALYTICS_INTERVAL = + parseInt(process.env.FILL_QUALITY_ANALYTICS_INTERVAL) || 300_000; // 5 minutes default +const FILL_QUALITY_ANALYTICS_LOOKBACK_MS = + parseInt(process.env.FILL_QUALITY_ANALYTICS_LOOKBACK_MS) || 86_400_000; // 24 hours default +const FILL_QUALITY_ANALYTICS_SMOOTHING_MINUTES = + parseInt(process.env.FILL_QUALITY_ANALYTICS_SMOOTHING_MINUTES) || 60; + // TOB monitoring configuration - defaults to true if not set const ENABLE_TOB_MONITORING = !process.env.ENABLE_TOB_MONITORING || @@ -169,6 +187,22 @@ if (ENABLE_TOB_MONITORING) { `TOB Monitoring Markets: ${TOB_MONITORING_ENABLED_PERP_MARKETS.join(', ')}` ); } +logger.info( + `Fill Quality Analytics: ${ + ENABLE_FILL_QUALITY_ANALYTICS ? 'enabled' : 'disabled' + }` +); +if (ENABLE_FILL_QUALITY_ANALYTICS) { + logger.info( + `Fill Quality Analytics Interval: ${FILL_QUALITY_ANALYTICS_INTERVAL}ms` + ); + logger.info( + `Fill Quality Analytics Lookback: ${FILL_QUALITY_ANALYTICS_LOOKBACK_MS}ms` + ); + logger.info( + `Fill Quality Analytics Smoothing: ${FILL_QUALITY_ANALYTICS_SMOOTHING_MINUTES} minutes` + ); +} let MARKET_SUBSCRIBERS: SubscriberLookup = {}; @@ -504,8 +538,7 @@ const main = async () => { 'grpc.keepalive_timeout_ms': 1_000, 'grpc.keepalive_permit_without_calls': 1, }, - client: grpcClient - + client: grpcClient, }, commitment: stateCommitment, }; @@ -817,6 +850,196 @@ const main = async () => { // Start TOB monitoring setInterval(checkTobForStuckOrders, TOB_CHECK_INTERVAL); + // Track last known values for fill quality metrics (per market/side/cohort) + const lastFillQualityValues = new Map(); + + // Helper function to update fill quality metric with null tracking + const updateFillQualityMetric = ( + value: string | number | null | undefined, + marketIndex: string | number, + side: string, + cohort: string + ) => { + const marketIndexStr = marketIndex.toString(); + const key = `${marketIndexStr}:${side}:${cohort}`; + const isNull = value === null || value === undefined; + + let valueToUse: number; + if (isNull) { + // Use last known value, or 0 if no previous value exists + valueToUse = lastFillQualityValues.get(key) || 0; + logger.debug( + `Null value for market ${marketIndexStr} ${side} ${cohort}, using last known value: ${valueToUse}` + ); + } else { + // Use current value and update the last known value + valueToUse = Number(value); + lastFillQualityValues.set(key, valueToUse); + } + + takerFillBpsFromOracleGauge.setLatestValue(valueToUse, { + marketIndex: marketIndexStr, + side, + cohort, + null: isNull ? 'true' : 'false', + }); + }; + + // Fill Quality Analytics function - fetch and store in Redis + const fetchAndStoreFillQualityAnalytics = async () => { + if (!ENABLE_FILL_QUALITY_ANALYTICS) { + return; + } + + try { + logger.info('Starting fill quality analytics fetch'); + const startTime = Date.now(); + + const fillQualityRepo = FillQualityAnalyticsRepository(); + const toMs = Date.now(); + const fromMs = toMs - FILL_QUALITY_ANALYTICS_LOOKBACK_MS; + + const results = await fillQualityRepo.getTakerFillVsOracleBps( + fromMs, + toMs, + 9, // baseDecimals + FILL_QUALITY_ANALYTICS_SMOOTHING_MINUTES + ); + + logger.info( + `Fetched fill quality analytics for ${results.length} markets in ${ + Date.now() - startTime + }ms` + ); + + // Store results in Redis - one key per market + const startRedisSet = Date.now(); + for (const result of results) { + const redisKey = `taker_fill_vs_oracle_bps:market:${result.MarketIndex}`; + const redisValue = JSON.stringify({ + marketIndex: result.MarketIndex, + takerBuyBpsFromOracle: { + all: result.TakerBuyBpsFromOracle_ALL, + '1e0': result.TakerBuyBpsFromOracle_1e0, + '1e3': result.TakerBuyBpsFromOracle_1e3, + '1e4': result.TakerBuyBpsFromOracle_1e4, + '1e5': result.TakerBuyBpsFromOracle_1e5, + '1e6': result.TakerBuyBpsFromOracle_1e6, + }, + takerSellBpsFromOracle: { + all: result.TakerSellBpsFromOracle_ALL, + '1e0': result.TakerSellBpsFromOracle_1e0, + '1e3': result.TakerSellBpsFromOracle_1e3, + '1e4': result.TakerSellBpsFromOracle_1e4, + '1e5': result.TakerSellBpsFromOracle_1e5, + '1e6': result.TakerSellBpsFromOracle_1e6, + }, + updatedAtTs: Date.now(), + } as TakerFillVsOracleBpsRedisResult); + + await redisClient.set(redisKey, redisValue); + + updateFillQualityMetric( + result.TakerBuyBpsFromOracle_ALL, + result.MarketIndex, + 'buy', + 'all' + ); + updateFillQualityMetric( + result.TakerBuyBpsFromOracle_1e0, + result.MarketIndex, + 'buy', + '1e0' + ); + updateFillQualityMetric( + result.TakerBuyBpsFromOracle_1e3, + result.MarketIndex, + 'buy', + '1e3' + ); + updateFillQualityMetric( + result.TakerBuyBpsFromOracle_1e4, + result.MarketIndex, + 'buy', + '1e4' + ); + updateFillQualityMetric( + result.TakerBuyBpsFromOracle_1e5, + result.MarketIndex, + 'buy', + '1e5' + ); + updateFillQualityMetric( + result.TakerBuyBpsFromOracle_1e6, + result.MarketIndex, + 'buy', + '1e6' + ); + updateFillQualityMetric( + result.TakerSellBpsFromOracle_ALL, + result.MarketIndex, + 'sell', + 'all' + ); + updateFillQualityMetric( + result.TakerSellBpsFromOracle_1e0, + result.MarketIndex, + 'sell', + '1e0' + ); + updateFillQualityMetric( + result.TakerSellBpsFromOracle_1e3, + result.MarketIndex, + 'sell', + '1e3' + ); + updateFillQualityMetric( + result.TakerSellBpsFromOracle_1e4, + result.MarketIndex, + 'sell', + '1e4' + ); + updateFillQualityMetric( + result.TakerSellBpsFromOracle_1e5, + result.MarketIndex, + 'sell', + '1e5' + ); + updateFillQualityMetric( + result.TakerSellBpsFromOracle_1e6, + result.MarketIndex, + 'sell', + '1e6' + ); + + logger.debug( + `Stored fill quality analytics for market ${result.MarketIndex}` + ); + } + logger.info( + `Successfully stored fill quality analytics for ${ + results.length + } markets in Redis in ${Date.now() - startRedisSet}ms` + ); + } catch (error) { + logger.error('Error fetching/storing fill quality analytics:', error); + } + }; + + // Run fill quality analytics fetch immediately on startup, then on interval + if (ENABLE_FILL_QUALITY_ANALYTICS) { + fetchAndStoreFillQualityAnalytics().catch((error) => { + logger.error('Initial fill quality analytics fetch failed:', error); + }); + setInterval( + fetchAndStoreFillQualityAnalytics, + FILL_QUALITY_ANALYTICS_INTERVAL + ); + logger.info( + `Fill quality analytics will run every ${FILL_QUALITY_ANALYTICS_INTERVAL}ms` + ); + } + const handleStartup = async (_req, res, _next) => { if (driftClient.isSubscribed && dlobProvider.size() > 0) { res.writeHead(200); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 9b3eea1..ec0f7ae 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -34,6 +34,7 @@ import { wsMarketArgs } from 'src/dlob-subscriber/DLOBSubscriberIO'; import { DEFAULT_AUCTION_PARAMS, MID_MAJOR_MARKETS } from './constants'; import { AuctionParamArgs } from './types'; import { COMMON_MATH, ENUM_UTILS } from '@drift/common'; +import { TakerFillVsOracleBpsRedisResult } from '../athena/repositories/fillQualityAnalytics'; export const GROUPING_OPTIONS = [1, 10, 100, 500, 1000]; export const GROUPING_DEPENDENCIES = { @@ -312,10 +313,17 @@ export function publishGroupings( asks: aggregatedAsks, }); - - if(['SOL-PERP', 'BTC-PERP', 'ETH-PERP'].includes(l2Formatted_grouped20.marketName) && aggregatedBids.length !== 20 || aggregatedAsks.length !== 20) { - logger.error(`Error aggregating dlob levels: group=${group}, bids=${fullAggregatedBids.length}, asks=${fullAggregatedAsks.length}`) - logger.error(`Response: ${JSON.stringify(l2Formatted_grouped20)}`) + if ( + (['SOL-PERP', 'BTC-PERP', 'ETH-PERP'].includes( + l2Formatted_grouped20.marketName + ) && + aggregatedBids.length !== 20) || + aggregatedAsks.length !== 20 + ) { + logger.error( + `Error aggregating dlob levels: group=${group}, bids=${fullAggregatedBids.length}, asks=${fullAggregatedAsks.length}` + ); + logger.error(`Response: ${JSON.stringify(l2Formatted_grouped20)}`); } redisClient.publish( @@ -911,6 +919,8 @@ export const getEstimatedPrices = async ( * @param driftClient - DriftClient instance (optional, for price calculation) * @param fetchFromRedis - Redis fetch function (optional, for price calculation) * @param selectMostRecentBySlot - Slot selection function (optional, for price calculation) + * @param fillQualityInfo - Fill quality analytics data (version 2 only) + * @param apiVersion - API version (1 or 2) * @returns Object formatted for deriveMarketOrderParams function or error response */ export const mapToMarketOrderParams = async ( @@ -920,7 +930,9 @@ export const mapToMarketOrderParams = async ( key: string, selectionCriteria: (responses: any) => any ) => Promise, - selectMostRecentBySlot?: (responses: any[]) => any + selectMostRecentBySlot?: (responses: any[]) => any, + fillQualityInfo?: TakerFillVsOracleBpsRedisResult, + apiVersion: number = 1 ): Promise<{ success: boolean; data?: { @@ -960,6 +972,15 @@ export const mapToMarketOrderParams = async ( let estimatedPrices; let processedSlippageTolerance = params.slippageTolerance; + // Track debug info for logging + let debugInfo = { + originalOraclePrice: null as string | null, + adjustedOraclePrice: null as string | null, + isCrossed: false, + fillQualityBps: null as number | null, + appliedV2Adjustment: false, + }; + if (driftClient && fetchFromRedis && selectMostRecentBySlot) { // Get L2 orderbook data using the utility function const redisL2 = await fetchL2FromRedis( @@ -980,6 +1001,73 @@ export const mapToMarketOrderParams = async ( redisL2 ); + // Store original oracle for debugging + debugInfo.originalOraclePrice = estimatedPrices.oraclePrice.toString(); + + // VERSION 2: Adjust oracle price based on fill quality when orderbook is crossed + if (apiVersion === 2 && fillQualityInfo && redisL2) { + try { + // Convert raw L2 to formatted L2 for cross detection + const l2Formatted = convertRawL2ToBN(redisL2); + + const isSpot = isVariant(marketType, 'spot'); + const oracleData = isSpot + ? driftClient.getOracleDataForSpotMarket(params.marketIndex) + : driftClient.getOracleDataForPerpMarket(params.marketIndex); + const oraclePrice = oracleData.price ?? ZERO; + + // Detect if orderbook is crossed + const spreadInfo = COMMON_MATH.calculateSpreadBidAskMark( + l2Formatted, + oraclePrice + ); + + const isCrossed = + spreadInfo.bestBidPrice && + spreadInfo.bestAskPrice && + spreadInfo.bestBidPrice.gte(spreadInfo.bestAskPrice); + + debugInfo.isCrossed = isCrossed; + + if (isCrossed) { + // Get fill quality metric based on direction + const fillQualityBpsStr = + direction === PositionDirection.LONG + ? fillQualityInfo.takerBuyBpsFromOracle?.all + : fillQualityInfo.takerSellBpsFromOracle?.all; + + if ( + fillQualityBpsStr && + fillQualityBpsStr !== 'null' && + fillQualityBpsStr !== null + ) { + const fillQualityBps = Math.round( + parseFloat(fillQualityBpsStr) * 100 + ); // mul 100 to maintain 2 sigfigs (1.23 bps = 0.000123) + debugInfo.fillQualityBps = fillQualityBps; + + if (!isNaN(fillQualityBps)) { + const adjustment = oraclePrice + .muln(fillQualityBps) + .divn(10000 * 100); // adjustmentFactor is in bps + const adjustedOraclePrice = oraclePrice.add(adjustment); + + // Update the oracle price in estimatedPrices + estimatedPrices.oraclePrice = adjustedOraclePrice; + debugInfo.adjustedOraclePrice = adjustedOraclePrice.toString(); + debugInfo.appliedV2Adjustment = true; + } + } + } + } catch (error) { + logger.warn( + 'Version 2: Failed to apply fill quality adjustment, using standard oracle:', + error + ); + // Fall through to use unadjusted oracle + } + } + // Handle dynamic slippage tolerance calculation if needed if (params.slippageTolerance === undefined) { // Convert raw L2 to formatted L2 for slippage calculation @@ -1028,6 +1116,62 @@ export const mapToMarketOrderParams = async ( baseAmount = amount.mul(BASE_PRECISION).div(estimatedPrices.entryPrice); } + // Comprehensive debug logging + logger.info( + JSON.stringify({ + event: 'auction_params_calculated', + requestParams: { + marketIndex: params.marketIndex, + marketType: params.marketType, + direction: direction === PositionDirection.LONG ? 'long' : 'short', + amount: params.amount, + assetType: params.assetType, + slippageTolerance: params.slippageTolerance, + apiVersion, + }, + priceDiscovery: { + originalOraclePrice: debugInfo.originalOraclePrice, + finalOraclePrice: estimatedPrices.oraclePrice.toString(), + bestPrice: estimatedPrices.bestPrice.toString(), + entryPrice: estimatedPrices.entryPrice.toString(), + worstPrice: estimatedPrices.worstPrice.toString(), + markPrice: estimatedPrices.markPrice.toString(), + priceImpactBps: estimatedPrices.priceImpact.toString(), + }, + v2CrossDetection: + apiVersion === 2 + ? { + isCrossed: debugInfo.isCrossed, + fillQualityBps: debugInfo.fillQualityBps, + adjustedOraclePrice: debugInfo.adjustedOraclePrice, + appliedAdjustment: debugInfo.appliedV2Adjustment, + fillQualityData: fillQualityInfo + ? { + takerBuyBpsAll: fillQualityInfo.takerBuyBpsFromOracle?.all, + takerSellBpsAll: + fillQualityInfo.takerSellBpsFromOracle?.all, + } + : null, + } + : undefined, + auctionConfig: { + duration: params.auctionDuration, + startPriceOffset: params.auctionStartPriceOffset, + startPriceOffsetFrom: params.auctionStartPriceOffsetFrom, + endPriceOffset: params.auctionEndPriceOffset, + endPriceOffsetFrom: params.auctionEndPriceOffsetFrom, + slippageTolerance: processedSlippageTolerance, + isOracleOrder: params.isOracleOrder, + reduceOnly: params.reduceOnly ?? false, + allowInfSlippage: params.allowInfSlippage ?? false, + }, + calculatedValues: { + baseAmount: baseAmount.toString(), + slippageToleranceFinal: processedSlippageTolerance, + }, + }) + ); + return { success: true, data: { From 2426720fbcadb518f8f91a885e28b90f95b62b97 Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 22 Oct 2025 13:48:05 -0700 Subject: [PATCH 4/4] prettify --- src/athena/README.md | 348 ------------------------------------------- src/utils/utils.ts | 4 +- 2 files changed, 2 insertions(+), 350 deletions(-) delete mode 100644 src/athena/README.md diff --git a/src/athena/README.md b/src/athena/README.md deleted file mode 100644 index ebe9a2f..0000000 --- a/src/athena/README.md +++ /dev/null @@ -1,348 +0,0 @@ -# Athena Integration - -This module provides connectivity to AWS Athena for querying historical Drift protocol data. - -## Setup - -### Prerequisites - -1. AWS credentials configured with access to Athena -2. Environment variables: - - `AWS_REGION` (optional, defaults to `us-east-1`) - - `AWS_ACCESS_KEY_ID` (required) - - `AWS_SECRET_ACCESS_KEY` (required) - - `ATHENA_DATABASE` (optional, defaults to `staging-archive`) - - `ATHENA_OUTPUT_BUCKET` (optional, defaults to `staging-data-ingestion-bucket`) - -### Installation - -The required AWS SDK packages are already included in `package.json`: -```bash -yarn install -``` - -## Usage - -### Basic Query Example - -```typescript -import { Athena } from './athena/client'; - -const { query } = Athena(); - -const results = await query(` - SELECT * FROM eventtype_traderecord - WHERE markettype = 'perp' - LIMIT 10 -`); - -console.log(results); -``` - -### Using the Fill Quality Analytics Repository - -```typescript -import { FillQualityAnalyticsRepository } from './athena/repositories/fillQualityAnalytics'; - -const repository = FillQualityAnalyticsRepository(); - -// Get taker fill vs oracle basis points for ALL perp markets -// Time range: last 7 days -const nowMs = Date.now(); -const weekAgoMs = nowMs - 7 * 24 * 60 * 60 * 1000; - -const results = await repository.getTakerFillVsOracleBps( - weekAgoMs, // from (unix timestamp in milliseconds) - nowMs, // to (unix timestamp in milliseconds) - 9, // baseDecimals (default: 9) - 60 // smoothingMinutes (default: 60) -); - -// Results contain time series data with columns: -// - Time: timestamp -// - MarketIndex: the perp market index -// - TakerBuyBpsFromOracle_1e0: average for buy orders < $1000 -// - TakerBuyBpsFromOracle_1e3: average for buy orders $1k-$10k -// - TakerBuyBpsFromOracle_1e4: average for buy orders $10k-$100k -// - TakerBuyBpsFromOracle_1e5: average for buy orders $100k-$1M -// - TakerBuyBpsFromOracle_1e6: average for buy orders > $1M -// - (Similar columns for sell orders) -// - Zero: baseline (always 0) - -// Data is partitioned by MarketIndex, so you can filter/group by market -const solPerpData = results.filter(r => r.MarketIndex === '0'); -console.log(solPerpData); -``` - -### Batch Queries - -```typescript -import { Athena } from './athena/client'; - -const { batchQuery } = Athena(); - -const { results, errors } = await batchQuery({ - queries: [ - { query: 'SELECT COUNT(*) FROM eventtype_traderecord' }, - { query: 'SELECT COUNT(*) FROM eventtype_orderrecord' }, - ] -}); - -console.log('Results:', results); -console.log('Errors:', errors); -``` - -### Custom Database/Bucket - -```typescript -import { Athena } from './athena/client'; - -const { query } = Athena({ - overrideDatabaseName: 'my_custom_db', - overrideBucketName: 'my-custom-bucket' -}); -``` - -## Architecture - -### Client (`client.ts`) -- Handles Athena query execution -- Manages query lifecycle (start, poll, retrieve results) -- Implements rate limiting via Bottleneck -- Provides batch query support - -### Utilities (`utils.ts`) -- `getTimePartition`: Generate time partition filters for queries -- `getTimeRangeAndPartitions`: Generate time range and partition filters - -### Repositories -- **Fill Quality Analytics** (`repositories/fillQualityAnalytics.ts`): Analyzes taker fill quality vs oracle prices, bucketed by order size cohorts - -## Query Best Practices - -1. **Always use partition filters** to minimize data scanned: - ```sql - WHERE year = '2024' AND month = '01' AND day = '15' - ``` - -2. **Use the utility functions** for generating time-based filters: - ```typescript - import { getTimeRangeAndPartitions } from './athena/utils'; - - const query = ` - ${getTimeRangeAndPartitions(from, to)} - SELECT * FROM eventtype_traderecord - JOIN valid_partitions vp ON ... - `; - ``` - -3. **Monitor costs**: Each query logs bytes scanned. Athena charges per TB scanned. - -4. **Use batch queries** when running multiple independent queries to improve throughput. - -## Error Handling - -The client automatically retries failed queries up to 3 times with exponential backoff. Queries timeout after 5 minutes (300s) by default. - -```typescript -try { - const results = await query(queryString); -} catch (error) { - console.error('Query failed:', error.message); - // Handle error appropriately -} -``` - -## Adding New Repositories - -To add a new analytics repository: - -1. Create a new file in `repositories/` (e.g., `myAnalytics.ts`) -2. Define TypeScript interfaces for your results -3. Create a repository function that uses the Athena client -4. Export from `index.ts` - -Example: - -```typescript -// repositories/myAnalytics.ts -import { Athena } from '../client'; - -export interface MyResult { - field1: string; - field2: string; -} - -export const MyAnalyticsRepository = () => { - const { query } = Athena(); - - const getMyData = async (from: number, to: number): Promise => { - const queryString = ` - SELECT field1, field2 - FROM my_table - WHERE ts BETWEEN ${from} AND ${to} - `; - - const results = await query(queryString); - - return results.map((r) => ({ - field1: r.field1 || '', - field2: r.field2 || '', - })); - }; - - return { getMyData }; -}; -``` - -```typescript -// index.ts -export * from './repositories/myAnalytics'; -``` - -## Integration with DLOBPublisher - -The DLOBPublisher (`src/publishers/dlobPublisher.ts`) includes automatic fetching and caching of fill quality analytics data to Redis. - -### Configuration - -Enable fill quality analytics via environment variables: - -```bash -# Enable the feature (default: false) -ENABLE_FILL_QUALITY_ANALYTICS=true - -# How often to fetch and update analytics (default: 300000 = 5 minutes) -FILL_QUALITY_ANALYTICS_INTERVAL=300000 - -# Historical data lookback window (default: 86400000 = 24 hours) -FILL_QUALITY_ANALYTICS_LOOKBACK_MS=86400000 - -# Rolling average smoothing window in minutes (default: 60) -FILL_QUALITY_ANALYTICS_SMOOTHING_MINUTES=60 -``` - -### How it Works - -1. **On Startup**: Immediately fetches fill quality analytics for all perp markets -2. **Periodic Updates**: Re-fetches data every `FILL_QUALITY_ANALYTICS_INTERVAL` milliseconds -3. **Redis Storage**: Stores results in Redis with the following keys: - - `taker_fill_vs_oracle_bps:market:{marketIndex}` - Individual market data - - `taker_fill_vs_oracle_bps:summary` - Summary with metadata - -### Redis Data Format - -Per-market keys contain: -```json -{ - "time": "2024-01-15 10:30:00.000", - "marketIndex": "0", - "takerBuyBpsFromOracle": { - "all": "5.23", - "1e0": "8.45", - "1e3": "6.12", - "1e4": "4.89", - "1e5": "3.56", - "1e6": "2.34" - }, - "takerSellBpsFromOracle": { - "all": "-4.87", - "1e0": "-7.23", - "1e3": "-5.45", - "1e4": "-4.12", - "1e5": "-3.01", - "1e6": "-2.15" - }, - "updatedAt": "2024-01-15T10:35:00.000Z" -} -``` - -Summary key contains: -```json -{ - "markets": ["0", "1", "2"], - "lastUpdated": "2024-01-15T10:35:00.000Z", - "lookbackMs": 86400000, - "smoothingMinutes": 60 -} -``` - -### Accessing the Data - -Downstream services can read the cached analytics from Redis without querying Athena directly: - -```typescript -import { RedisClient } from '@drift/common/clients'; - -const redis = new RedisClient(); -await redis.connect(); - -// Get summary -const summary = await redis.get('taker_fill_vs_oracle_bps:summary'); -console.log(JSON.parse(summary)); - -// Get data for SOL-PERP (market index 0) -const solData = await redis.get('taker_fill_vs_oracle_bps:market:0'); -console.log(JSON.parse(solData)); -``` - -This architecture allows multiple services to access expensive analytics queries without hitting Athena repeatedly. - -## Testing - -### Test Redis Polling - -To verify that fill quality analytics data is being written to and updated in Redis: - -```bash -# Start the DLOBPublisher with fill quality analytics enabled -ENABLE_FILL_QUALITY_ANALYTICS=true yarn dlob-publish - -# In another terminal, run the Redis polling test -yarn test:fill-quality-redis -``` - -The polling test script will: -- Connect to Redis -- Poll every 10 seconds for markets 0, 1, 2 (SOL, BTC, ETH) -- Display the summary and per-market data -- Track when data is updated (shows 🆕 NEW UPDATE when timestamps change) -- Validate data structure integrity -- Show data age and warn if stale (>10 minutes old) - -**Example output:** -``` -========== Polling iteration 1 ========== - -📋 Summary: - Markets available: 0, 1, 2 - Last updated: 2024-01-15T10:35:00.000Z - Lookback: 86400000ms (24h) - Smoothing: 60 minutes - -📊 Market Data: - 🆕 NEW UPDATE - SOL-PERP (Market 0) - Data timestamp: 2024-01-15 10:30:00.000 - Updated at: 2024-01-15T10:35:00.000Z - Age: 15s ago - Taker Buy (all): 5.23 bps - Taker Sell (all): -4.87 bps - Large orders (>$1M): Buy=2.34bps, Sell=-2.15bps - - 🆕 NEW UPDATE - BTC-PERP (Market 1) - Data timestamp: 2024-01-15 10:30:00.000 - Updated at: 2024-01-15T10:35:00.000Z - Age: 15s ago - Taker Buy (all): 4.12 bps - Taker Sell (all): -3.89 bps - Large orders (>$1M): Buy=1.89bps, Sell=-1.76bps - -📈 Update Tracking: - ✅ All 3 markets have data - -🔍 Validation: - ✅ All data structures are valid -``` - -The script will continue polling until you press `Ctrl+C`. - diff --git a/src/utils/utils.ts b/src/utils/utils.ts index ec0f7ae..61a34bf 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -973,7 +973,7 @@ export const mapToMarketOrderParams = async ( let processedSlippageTolerance = params.slippageTolerance; // Track debug info for logging - let debugInfo = { + const debugInfo = { originalOraclePrice: null as string | null, adjustedOraclePrice: null as string | null, isCrossed: false, @@ -1043,7 +1043,7 @@ export const mapToMarketOrderParams = async ( ) { const fillQualityBps = Math.round( parseFloat(fillQualityBpsStr) * 100 - ); // mul 100 to maintain 2 sigfigs (1.23 bps = 0.000123) + ); debugInfo.fillQualityBps = fillQualityBps; if (!isNaN(fillQualityBps)) {