Skip to content

feat(keys): add Time-Weighted Average Price (TWAP) endpoint - #889

Closed
TS-mfon wants to merge 2 commits into
accesslayerorg:mainfrom
TS-mfon:fix/issue-866-twap-endpoint
Closed

TS-mfon wants to merge 2 commits into
accesslayerorg:mainfrom
TS-mfon:fix/issue-866-twap-endpoint

Conversation

@TS-mfon

@TS-mfon TS-mfon commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements a Time-Weighted Average Price (TWAP) endpoint for creator keys, enabling clients to retrieve moving-average benchmark pricing over configured time windows with Redis caching.

Closes #866


Implementation Details

  • Endpoint: GET /api/v1/keys/:keyId/twap?window=1h|24h|7d
  • Calculation Algorithm:
    • Fetches historical price snapshots within the requested lookback window.
    • Applies trapezoidal numerical integration over snapshot intervals: $\bar{P}i = \frac{P_i + P{i+1}}{2}$, $\text{TWAP} = \frac{\sum \bar{P}_i \cdot \Delta t_i}{\sum \Delta t_i}$.
    • When fewer than 2 snapshots exist within the window, falls back to the current spot price.
  • Caching: Results cached in Redis with a 60-second TTL (cache:keys:twap:{keyId}:{window}).
  • Cache Invalidation: Invokes invalidateKeyTwapCache when new trades are processed in trade-indexer.service.ts and when new price snapshots are written in price-snapshot.service.ts.
  • Validation: Enforces valid window parameter (1h, 24h, 7d), returning 422 for unsupported values.

Testing

  • Unit test suite added in src/modules/keys/__tests__/key-twap.test.ts verifying:
    • 422 returned for invalid or missing window parameter.
    • 404 returned for unknown key.
    • Spot price returned when insufficient snapshots exist.
    • Exact TWAP calculation over snapshot intervals for 24h window.
    • 60-second Redis caching and eviction upon invalidateKeyTwapCache.
  • All 5 tests pass; typechecks and build pass.

Copilot AI lite review requested due to automatic review settings September 24, 2026 16:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Address TWAP coverage, contract-view integration, cache invalidation, and documented fallback inconsistencies.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 2 Medium severity · 1 Low severity

Open (4)
What changed in this PR

Adds a cached TWAP endpoint for creator keys using historical price snapshots.

Changes:

  • Adds validated TWAP route and calculation service.
  • Implements Redis caching and invalidation hooks.
  • Adds endpoint and caching tests.
File Summary
src/​modules/​keys/​keys.routes.ts Registers the TWAP endpoint.
src/​modules/​keys/​key-twap.service.ts Calculates and caches TWAP responses.
src/​modules/​keys/​__tests__/​key-twap.test.ts Tests validation, calculation, caching, and invalidation.
src/​modules/​indexer/​trade-indexer.service.ts Invalidates TWAP caches after trades.
src/​modules/​indexer/​price-snapshot.service.ts Invalidates TWAP caches after price updates.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +92 to +96
for (let i = 0; i < snapshots.length - 1; i++) {
const tCurrent = snapshots[i].recordedAt.getTime();
const tNext = snapshots[i + 1].recordedAt.getTime();
const dt = Math.max(0, tNext - tCurrent);

Comment on lines +113 to +118
try {
const { invalidateKeyTwapCache } = await import('../keys/key-twap.service');
await invalidateKeyTwapCache(creatorId);
} catch {
// Non-critical cache invalidation failure
}
Comment on lines +55 to +59
// Log get_twap contract view call with window in ledger units
logger.info(
{
operation: 'get_twap_contract_view',
keyId: creatorId,
Comment on lines +22 to +24
cacheSetJson: jest.fn(async (key: string, value: unknown) => {
redisStore.set(key, JSON.stringify(value));
}),
Closes accesslayerorg#866

- Add GET /api/v1/keys/:keyId/twap supporting 1h, 24h, and 7d time windows
- Compute TWAP via trapezoidal integration over historical price snapshot intervals
- Cache calculation in Redis for 60 seconds
- Invalidate TWAP cache on new trades and price snapshot writes
- Validate window query parameters with 422 error on invalid inputs
@TS-mfon

TS-mfon commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1007, which carries all four issues (#866, #869, #870, #872) on one branch rebased on latest main.

Splitting these four across separate PRs meant they collided with each other: all four touch src/modules/keys/keys.routes.ts, and #869/#872 both touch src/modules/creator/creator.routes.ts and the prisma schema, so whichever merged first would break the rest. #1007 also addresses the Copilot findings raised here — the migration moved to prisma/schema/migrations and non-finite milestone values are now rejected.

Closing this one in favour of #1007.

@TS-mfon TS-mfon closed this Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a TWAP price endpoint returning the time-weighted average price for a creator key over a configurable window

2 participants