/** * @pwngh/economy-lab * * Copyright (c) Preston Neal * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ import type { Operation, Outcome } from './contract.js'; import type { Config } from './config.js'; import type { Attempt, Velocity } from './ports.js'; import type { RejectionCode } from './errors.js'; export declare const VELOCITY_CURRENCY: "CREDIT"; /** * Reports the risk check's verdict: either allow, or deny with a reason. On a deny, `screenRisk` * in economy.ts's submit pipeline turns this into `rejected(reason, ...)` for the caller. The * reason is never raised as an error. */ export type RiskDecision = { allow: true; } | { allow: false; reason: RejectionCode; }; /** * Sums a subject's spending in the sliding window ending at `now` (`at > now - windowMs`); * attempts age out as the window slides, with no fixed reset boundary. * * This is the in-memory twin of the SQL stores' windowed `SUM(amount) WHERE at > cutoff`, so * every backend enforces the same rolling limit. The store deduplicates attempts before they * reach here, so each idempotency key counts once. `windowStart` is the earliest `at` still in * the window, or 0 when the window is empty. Only `spent` feeds the risk check. */ export declare function windowedVelocity(subject: string, attempts: ReadonlyArray, now: number, windowMs: number): Velocity; /** * Allows the operation unless its class's windowed total plus this operation's amount exceeds * that class's limit ({@link classLimitMinor}). The caller passes the `velocity` that the store * windowed on read, applying `config.velocityWindowMs`, so the comparison runs against the live * window. An operation that moves no tracked subject's funds is always allowed, which is the * case when `riskSubject` returns null. * * @see {@link https://economy-lab-docs.pages.dev/economy/concepts/spend-velocity/#the-idea Spend velocity} * for the rolling window, why denied attempts still count, and how the record survives a * rollback. */ export declare function assessRisk(velocity: Velocity, operation: Operation, config: Config): RiskDecision; /** * Builds the attempt record to add to a subject's running total after an operation finishes. * Returns null for an untracked subject or a duplicate already counted. The record carries * `idempotencyKey` so the store will not count a genuine retry twice. This is the pure reference * twin; the live pipeline records the equivalent through `store.trust.record` (see economy.ts * screenRisk), not this. * * A `rejected` outcome is still recorded, because denied attempts count toward the limit and a * burst is itself a fraud signal. A `duplicate` is not recorded, because the original already * counted. */ export declare function riskAttempt(operation: Operation, outcome: Outcome, at: number): Attempt | null; /** Which velocity window an attempt fills: value flowing into the wallet, or out of it. */ export type RiskClass = 'in' | 'out'; /** * Returns the trust-store subject and window class this operation counts against, or null when * it is not risk-checked. Inflow and outflow are different threat models (card testing fills one, * a drained wallet the other), so each class keeps its own window: the recorded subject is * `:`. * * This is the single source of the subject rule. The live pipeline check (economy.ts screenRisk) * calls `riskSubject` and `attemptMinor` directly; `assessRisk` and `riskAttempt` are the * test-facing pure twins. The guarantee is shared logic, not a shared call path. */ export declare function riskSubject(operation: Operation): { subject: string; class: RiskClass; } | null; /** * The limit (CREDIT minor units) governing one window class. Each class falls back to the * single-knob `velocityLimitMinor` unless its own limit is set, so one figure still configures * both windows and a deployment that needs different in/out ceilings sets them apart. */ export declare function classLimitMinor(config: Config, cls: RiskClass): bigint; /** * Returns how much this operation adds to its subject's running total, in CREDIT minor units. * Returns 0 for an operation that moves no tracked funds. */ export declare function attemptMinor(operation: Operation): bigint;