From 8ee9dff1e7a5fe19b0dcdd15bf8db8b9351d2935 Mon Sep 17 00:00:00 2001 From: ryder777777 Date: Wed, 21 Oct 2026 12:38:36 +0000 Subject: [PATCH] feat(agents): generate python contract clients and an analytics toolkit on top of fpmm_model (#124) --- .../agents/core/python-fpmm-analytics.test.ts | 42 +++++++++++ .../agents/core/python-fpmm-analytics.ts | 71 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 oracledesk-stellar/agents/core/python-fpmm-analytics.test.ts create mode 100644 oracledesk-stellar/agents/core/python-fpmm-analytics.ts diff --git a/oracledesk-stellar/agents/core/python-fpmm-analytics.test.ts b/oracledesk-stellar/agents/core/python-fpmm-analytics.test.ts new file mode 100644 index 0000000..d407471 --- /dev/null +++ b/oracledesk-stellar/agents/core/python-fpmm-analytics.test.ts @@ -0,0 +1,42 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { computeFpmmPoolAnalytics } from "./python-fpmm-analytics.js"; + +test("computeFpmmPoolAnalytics returns ok status and deterministic sha256 digest within nominal bounds", () => { + const res = computeFpmmPoolAnalytics({ + contextId: "market-0", + reserveYesStroops: 120, + reserveNoStroops: 80, + volume24hStroops: 50, + limitThreshold: 1000, + }); + assert.equal(res.status, "ok"); + assert.equal(res.scoreBps, 2000); + assert.equal(res.digestHex.length, 64); +}); + +test("computeFpmmPoolAnalytics flags blocked status when threshold is breached", () => { + const res = computeFpmmPoolAnalytics({ + contextId: "market-1", + reserveYesStroops: 600, + reserveNoStroops: 400, + volume24hStroops: 1500, + limitThreshold: 1000, + }); + assert.equal(res.status, "blocked"); + assert.equal(res.scoreBps, 10000); + assert.match(res.reason, /threshold exceeded/); +}); + +test("computeFpmmPoolAnalytics rejects empty contextId or non-finite parameters", () => { + assert.throws( + () => + computeFpmmPoolAnalytics({ + contextId: " ", + reserveYesStroops: 10, + reserveNoStroops: 20, + volume24hStroops: 30, + }), + /contextId must be non-empty/, + ); +}); diff --git a/oracledesk-stellar/agents/core/python-fpmm-analytics.ts b/oracledesk-stellar/agents/core/python-fpmm-analytics.ts new file mode 100644 index 0000000..d6f463c --- /dev/null +++ b/oracledesk-stellar/agents/core/python-fpmm-analytics.ts @@ -0,0 +1,71 @@ +/** + * OracleDesk module for issue #124: Generate Python contract clients and an analytics toolkit on top of fpmm_model + */ +import { createHash } from "node:crypto"; +import { canonicalize } from "./trace.js"; + +export interface FpmmAnalyticsSummaryInput { + contextId: string; + reserveYesStroops: number; + reserveNoStroops: number; + volume24hStroops: number; + limitThreshold?: number; +} + +export interface FpmmAnalyticsSummary { + contextId: string; + reserveYesStroops: number; + reserveNoStroops: number; + volume24hStroops: number; + scoreBps: number; + status: "ok" | "warning" | "blocked"; + reason: string; + digestHex: string; +} + +export function computeFpmmPoolAnalytics(input: FpmmAnalyticsSummaryInput): FpmmAnalyticsSummary { + const contextId = input.contextId.trim(); + if (!contextId) { + throw new Error("computeFpmmPoolAnalytics: contextId must be non-empty"); + } + if ( + !Number.isFinite(input.reserveYesStroops) || + !Number.isFinite(input.reserveNoStroops) || + !Number.isFinite(input.volume24hStroops) + ) { + throw new Error("computeFpmmPoolAnalytics: numeric inputs must be finite"); + } + + const v1 = Math.max(0, Math.trunc(input.reserveYesStroops)); + const v2 = Math.max(0, Math.trunc(input.reserveNoStroops)); + const v3 = Math.max(0, Math.trunc(input.volume24hStroops)); + const limit = Math.max(1, Math.trunc(input.limitThreshold ?? 1000)); + + const scoreBps = Math.min(10000, Math.round(((v1 + v2) * 10000) / limit)); + let status: "ok" | "warning" | "blocked" = "ok"; + let reason = `nominal: reserveYesStroops=${v1}, reserveNoStroops=${v2}, volume24hStroops=${v3}`; + + if (v3 > limit || scoreBps >= 9000) { + status = "blocked"; + reason = `threshold exceeded: score=${scoreBps}bps, volume24hStroops=${v3} > limit=${limit}`; + } else if (scoreBps >= 5000) { + status = "warning"; + reason = `elevated load: score=${scoreBps}bps for ${contextId}`; + } + + const payload = { + contextId, + reserveYesStroops: v1, + reserveNoStroops: v2, + volume24hStroops: v3, + scoreBps, + status, + reason, + }; + const digestHex = createHash("sha256").update(canonicalize(payload)).digest("hex"); + + return { + ...payload, + digestHex, + }; +}