Skip to content

Scoring Model and Formulas

RunTimeAdmin edited this page Oct 27, 2026 · 6 revisions

Scoring Model & Formulas

**Referenced Files in This Document** - [reputation-model.md](https://github.com/RunTimeAdmin/Sigvara/blob/main/docs/reputation-model.md) - [scoring.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/scoring.js) - [payments.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/payments.js) - [external.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/external.js) - [SigvaraReputation.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/SigvaraReputation.sol) - [scoring.test.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/scoring.test.js) - [payments.test.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/payments.test.js)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion

Introduction

This document explains Sigvara’s six-factor reputation scoring model and how it is computed, decayed, normalized, and enforced. The score is deterministic, bounded between 0 and 100, and composed of:

  • Fee Activity (20 points)
  • Success Rate (15 points)
  • Tenure (30 points)
  • External Trust (25 points)
  • Community (5 points)
  • Propagation (5 points)

The oracle computes the score off-chain and proposes it on-chain; the contract stores and serves the finalized score but does not compute it. A newly registered agent starts with no spendable score and must post a bond before any score can be proposed or finalized.

Section sources

Project Structure

The scoring model spans documentation, oracle logic, and on-chain storage:

graph TB
Docs["docs/reputation-model.md"]
OracleScoring["oracle/scoring.js"]
OraclePayments["oracle/payments.js"]
OracleExternal["oracle/external.js"]
Contract["src/SigvaraReputation.sol"]
Tests["oracle/scoring.test.js<br/>oracle/payments.test.js"]
Docs --> OracleScoring
OraclePayments --> OracleScoring
OracleExternal --> OracleScoring
OracleScoring --> Contract
Tests --> OracleScoring
Tests --> OraclePayments
Loading

Diagram sources

Section sources

Core Components

The six factors are pure functions in the oracle and validated by the contract:

Factor Max Points Purpose Key Inputs
Fee Activity 20 Real settled payment volume to the agent Decayed volume, fee unit, per-payer cap, trust-weighted counterparty standing
Success Rate 15 Outcome reported by a credentialed service (CounterAudit today) Decayed successful and total attestations, pseudo-observation prior
Tenure 30 Span of verified paid activity, faded by recency First and last verified payment timestamps, recency weight
External Trust 25 Normalized ERC-8004 feedback for a linked agent Recognized rating tags, normalization map, ownership verification
Community 5 Penalty from unresolved flags Age-weighted unresolved flag count
Propagation 5 Inherited trust from counterparties’ external standing Distinct counterparties, their matured external scores

All six are live. Only a bonded agent is scored at all; a newly registered agent is PendingBond and cannot be scored until minimum stake is posted.

The caps are kept once, in oracle/score-caps.js, which scoring.js, payments.js and external.js all read. A test compares each with the matching MAX_*_SCORE constant in SigvaraReputation.sol, which reverts any proposal with a factor above its cap.

Section sources

Architecture Overview

The end-to-end flow is:

sequenceDiagram
participant Client as "Client / Counterparty"
participant Chain as "EVM Chain"
participant Oracle as "Oracle Network"
participant Payments as "payments.js"
participant Scoring as "scoring.js"
participant External as "external.js"
participant Contract as "SigvaraReputation.sol"
Client->>Chain : Payment settles to agent address
Chain-->>Oracle : Transfer logs and block data
Oracle->>Payments : verifyPayment() + decayedVolume()
Payments-->>Oracle : Payer, amount, settledAt, legs
Oracle->>External : Read ERC-8004 feedback
External-->>Oracle : Normalized externalScore
Oracle->>Scoring : computeScore(...)
Scoring-->>Oracle : Six factor scores and total
Oracle->>Contract : proposeReputation(didHash, data, evidenceRoot)
Contract-->>Contract : Challenge window opens
Contract-->>Client : finalizeReputation() after window
Contract-->>Client : getTotalScore() returns matured value
Loading

Diagram sources

Detailed Component Analysis

Fee Activity (20 points)

Fee Activity measures real economic activity rather than request volume. When payment verification is enabled, the oracle uses measured settled volume; otherwise it falls back to an attestation-count proxy.

Key behavior:

  • One point per configured fee unit of decayed volume.
  • Each payment decays exponentially from its settlement time.
  • Any single counterparty contributes at most a capped number of points: 4 by default, so five payers with no standing of their own fill the factor.
  • A trusted counterparty’s cap is raised proportionally to its hard external standing.

Formulas and mechanics:

  • Decayed volume sums each payment weighted by 0.5 ^ (age / half-life).
  • Diversified volume applies a per-payer cap before converting to points.
  • Points are min(20, floor(decayedVolume / feeUnit)).

Rationale:

  • Volume is the hardest factor to fake because it requires genuine transfers to an address the operator does not control, from separately funded wallets.
  • Per-payer caps prevent one wallet from representing a broad customer base.
  • Trust-weighted caps reward counterparties that themselves hold external standing.
flowchart TD
Start(["Fee Activity Calculation"]) --> Verify["Verify Settlement Transaction"]
Verify --> Extract["Extract Transfers to Agent Address"]
Extract --> Group["Group by Payer"]
Group --> Decay["Apply Exponential Decay by Settled Age"]
Decay --> CapPayer["Cap Each Payer's Contribution"]
CapPayer --> Sum["Sum Capped Decayed Volume"]
Sum --> Convert["Convert to Points via Fee Unit"]
Convert --> Clamp["Clamp to 0–20"]
Clamp --> End(["Return feeScore"])
Loading

Diagram sources

Section sources

Success Rate (15 points)

Success Rate measures outcome quality reported by credentialed services, such as CounterAudit, which audits the work. It divides decayed successful attestations by decayed total attestations plus a pseudo-observation prior.

Key behavior:

  • Uses only attestations that carry an explicit success boolean.
  • Only a caller holding an oracle token records an outcome. A stranger's attestation with a verified payment credits the payment and records no outcome, so it counts toward fee volume and tenure but stays out of both sides of the ratio.
  • Both numerator and denominator decay together.
  • A prior of five observations prevents a single lucky job from matching a long perfect record.
  • As decayed weights tend to zero, the score tends toward zero, so silence costs.

Formula:

  • successScore = floor(successful / (total + 5) × 15)

Rationale:

  • The prior stabilizes small samples and ensures stale records fade.
  • The factor approaches 15 but never reaches it for finite evidence.
flowchart TD
Start(["Success Rate Calculation"]) --> Filter["Filter Attestations With Explicit Outcome"]
Filter --> Decay["Age-Weight Successful and Total"]
Decay --> Prior["Add Pseudo-Observation Prior"]
Prior --> Ratio["Compute Weighted Success Ratio"]
Ratio --> Scale["Scale to 0–15"]
Scale --> Floor["Floor to Integer"]
Floor --> End(["Return successScore"])
Loading

Diagram sources

Section sources

Tenure (30 points)

Tenure measures the span between an agent’s first and most recent verified payment, multiplied by how recent the last payment is. It is not calendar age since registration.

Key behavior:

  • If no verified payments exist and payment verification is on, tenure is zero regardless of registration date.
  • The logarithmic curve grows slowly: full marks require a long span.
  • Recency fades the factor once the agent stops working.
  • Without payment verification, the implementation falls back to calendar age for compatibility.

Formula:

  • ageCurve(days) = min(30, floor(log₂(spanDays + 1) × 3))
  • tenure = floor(ageCurve(spanDays) × clamp(recency, 0, 1))

Rationale:

  • Measuring activity instead of idle registration time removes free-lunch points.
  • Waiting then making one payment unlocks nothing because the span starts at first activity.
  • Abandoned history fades quickly, rewarding sustained operation.
flowchart TD
Start(["Tenure Calculation"]) --> Mode{"Payment Verification On?"}
Mode --> |No| Fallback["Calendar Curve on Age Since Registration"]
Mode --> |Yes| HasActivity{"Verified Payments Exist?"}
HasActivity --> |No| Zero["Tenure = 0"]
HasActivity --> |Yes| Span["Compute Span Days From First to Last Payment"]
Span --> Recency["Compute Recency From Last Payment Decay"]
Recency --> Curve["Apply Logarithmic Curve"]
Curve --> Multiply["Multiply By Clamped Recency"]
Multiply --> Floor["Floor Result"]
Fallback --> End(["Return ageScore"])
Zero --> End
Floor --> End
Loading

Diagram sources

Section sources

External Trust (25 points)

External Trust carries reputation already held under ERC-8004. The oracle reads recognized rating tags for a linked agent, normalizes them into a 0–1 scale, averages them, and scales to 25 points.

Key behavior:

  • Ownership of the ERC-8004 identity NFT is verified against the Sigvara operator.
  • Unrecognized tags are excluded rather than guessed.
  • The module is opt-in: without configuration, externalScore is zero.
  • CounterAudit publishes its feedback under its own counteraudit tag, which is not a recognised tag, so it is not counted here as well as in the success factor.

Normalization examples:

  • quality, trustscore, reliability, accuracy, credit: divide by 100 and clamp to 0–1.
  • win-rate: use directly and clamp to 0–1.
  • rating, e2e-test, stars: divide by 5 and clamp to 0–1.

Formula:

  • externalScore = round(mean(normalizedRecognizedTags) × 25)

Rationale:

  • External standing lives in a registry this protocol does not operate, so it cannot be minted by a Sybil ring controlled by the same attacker.
  • It is still not unbuyable: an attacker willing to build genuine ERC-8004 reputation can carry it here.
flowchart TD
Start(["External Trust Calculation"]) --> Enabled{"ERC-8004 Feed Enabled?"}
Enabled --> |No| Zero["Return 0"]
Enabled --> |Yes| Verify["Verify NFT Ownership by Operator"]
Verify --> |Fail| Zero
Verify --> |Pass| Read["Read Feedback Rows"]
Read --> Normalize["Normalize Recognized Tags to 0–1"]
Normalize --> Mean["Average Recognized Ratings"]
Mean --> Scale["Scale to 0–25"]
Scale --> Round["Round to Integer"]
Round --> End(["Return externalScore"])
Loading

Diagram sources

Section sources

Community (5 points)

Community is a baseline penalty based on unresolved flags.

Formula:

  • communityScore = max(0, floor(5 − unresolvedFlags × 2))

Behavior:

  • 0 flags → 5 points
  • 1 flag → 3 points
  • 2 flags → 1 point
  • 3+ flags → 0 points

Rationale:

  • Flags are reputational, separate from slashing.
  • The formula floors fractional age-weighted flags so penalties persist until the flag genuinely decays.

Section sources

Propagation (5 points)

Propagation is inherited trust from counterparties that themselves hold external standing. It counts distinct counterparties, pro-rated by their matured external score, capped at 5.

Key behavior:

  • One point per fully trusted counterparty, scaled by external standing.
  • Each counterparty counts once regardless of payment size.
  • Only the external part of a counterparty’s score is inherited, preventing farmed totals from laundering trust.
  • Unknown counterparties read as zero. A counterparty the node cannot get an answer for (throttled, timed out or dropped) is retried, and if the read still fails the agent is skipped for that epoch rather than scored on a missing standing.
  • Maturity lags earned standing, so reciprocal pairs cannot lift each other in one epoch.

Formula:

  • propagationScore = min(5, floor(sum over distinct counterparties of maturedExternalScore / 25))

Rationale:

  • Bootstrapping from fresh identities yields zero because they have no external standing.
  • Using matured external standing prevents reflexive amplification through wash trading.
flowchart TD
Start(["Propagation Calculation"]) --> Events["Collect Distinct Counterparties"]
Events --> Lookup["Lookup Matured External Score Per Counterparty"]
Lookup --> Sum["Sum Pro-Rated External Standing"]
Sum --> Cap["Cap at 5"]
Cap --> Floor["Floor to Integer"]
Floor --> End(["Return propagationScore"])
Loading

Diagram sources

Section sources

Decay Mechanisms, Half-Life, and Normalization

Decay is central to Fee Activity, Success Rate, Tenure, and Community signals.

  • Exponential decay: each event is weighted by 0.5 ^ (age / half-life).
  • Half-life defaults to 90 days for payment decay unless configured otherwise.
  • Decay is applied from the settlement timestamp, not the attestation submission time.
  • Success rate decays both numerator and denominator so a stale perfect ratio fades.
  • Tenure uses recency derived from the decay weight of the most recent payment.
  • Community flags are age-weighted and floored so penalties remain until decayed.
  • External trust normalization maps diverse tag scales to 0–1 before averaging and scaling to 25.
flowchart TD
Start(["Decay and Normalization"]) --> Age["Compute Age From Settled Timestamp"]
Age --> HalfLife["Apply Half-Life Configuration"]
HalfLife --> Weight["Compute Weight = 0.5^(age/half-life)"]
Weight --> Apply["Apply Weight To Volume, Attestations, Recency, Flags"]
Apply --> Normalize["Normalize External Tags to 0–1"]
Normalize --> Average["Average Recognized Tags"]
Average --> Scale["Scale to Factor Caps"]
Scale --> End(["Factor Scores"])
Loading

Diagram sources

Section sources

Rationale Behind Weight Assignments

Weights balance cheap-to-manufacture signals against hard-to-fake signals:

  • Fee Activity and Success Rate were reduced from higher values because wash rings can manufacture activity and self-reported outcomes.
  • Tenure was increased to reward sustained, arm’s-length paid operation that cannot be shortcut.
  • External Trust was increased because ERC-8004 standing lives outside this protocol and cannot be minted by a local Sybil ring.
  • Community and Propagation remain small but important: Community penalizes bad behavior, and Propagation rewards breadth of trusted counterparties without letting wash-traded totals amplify trust.

The design intentionally bounds cheap attacks; expensive attacks are bound by slashing and dispute mechanisms.

Section sources

Concrete Score Calculation Examples

Example 1: New Bonded Agent With No Activity

Inputs:

  • Registered now
  • No settled payments
  • No attestations
  • No flags
  • No ERC-8004 link
  • No counterparties

Expected factors:

  • Fee Activity: 0
  • Success Rate: 0
  • Tenure: 0
  • External Trust: 0
  • Community: 5
  • Propagation: 0
  • Earned total: 5

Maturity:

  • Spendable score starts below the earned score and climbs at the configured maturity rate per day.

Section sources

Example 2: Active Agent With Diverse Payers

Inputs:

  • Settled volume sufficient to reach near the fee cap
  • High success rate with accumulated attestations
  • Verified payment span approaching two years
  • No flags
  • Moderate ERC-8004 external standing
  • Several trusted counterparties

Expected behavior:

  • Fee Activity approaches 20 but may be limited by per-payer diversity.
  • Success Rate approaches 15 but does not reach it due to the prior.
  • Tenure grows logarithmically with span and recency.
  • External Trust reflects normalized ERC-8004 ratings.
  • Propagation increases with distinct trusted counterparties, capped at 5.

Section sources

Example 3: Stale Agent That Stopped Working

Inputs:

  • Long historical span
  • Recent silence
  • No new payments
  • No new attestations

Expected behavior:

  • Tenure decays because recency drops.
  • Success Rate decays because both numerator and denominator shrink.
  • Fee Activity decays because old payments lose weight.
  • Community and External Trust depend on flags and ERC-8004 status.

Section sources

Maturity Mechanism and Ramp-Up Period

Maturity prevents farm-and-cash-out attacks by releasing earned score gradually.

Key rules:

  • getEarnedScore returns the raw sum of factors.
  • getTotalScore returns the matured value, which climbs toward the earned score at maturityRatePerDay.
  • Falls apply immediately; rises are released over time.
  • A handover restarts maturity from zero while preserving the earned score.
  • A new agent starts with no spendable score and must post minimum stake before scoring begins.

On-chain consumption:

  • Consumers should call getTotalScore or meetsThreshold.
  • Threshold checks use the matured score.
stateDiagram-v2
[*] --> PendingBond
PendingBond --> Active : "Post minimumStake"
Active --> Suspended : "Withdrawal or slash pending"
Suspended --> Active : "Resume while bonded"
Active --> Slashed : "Slash executed"
Slashed --> [*]
Loading

Diagram sources

Section sources

Dependency Analysis

The scoring system has clear layering:

graph LR
Payments["payments.js"] --> Scoring["scoring.js"]
External["external.js"] --> Scoring["scoring.js"]
Scoring["scoring.js"] --> Contract["SigvaraReputation.sol"]
Docs["reputation-model.md"] --> Scoring["scoring.js"]
Docs --> Contract["SigvaraReputation.sol"]
Loading

Coupling and cohesion:

  • payments.js is responsible for chain verification, decay, per-payer capping, and activity windows.
  • external.js is responsible for ERC-8004 normalization and ownership checks.
  • scoring.js composes the six factors into a total score.
  • SigvaraReputation.sol enforces caps, challenge windows, maturity, and on-chain consumption.

Potential circular dependencies:

  • None detected; the oracle computes off-chain and writes to the contract.

External integration points:

  • EVM provider for transaction receipts and blocks.
  • ERC-8004 Reputation Registry for cross-protocol feedback.
  • Identity registry for agent state and stake view.

Section sources

Performance Considerations

  • Decay arithmetic uses scaled integers to preserve precision for 18-decimal tokens.
  • Half-life decay is exponential and applied per event; batching and grouping by payer reduce repeated computation.
  • Operators pass a fixed chain timestamp into scoring to ensure deterministic agreement across hosts. An epoch does not run when the chain's latest block time and the host clock differ by more than MAX_CLOCK_SKEW_SECONDS (600 by default).
  • Float drift is acknowledged; divergence tolerance exists today, but fixed-point decay is noted as a future improvement.
  • On-chain reads extrapolate maturity with wall-clock time so oracle outages do not pin honest agents below their earned score.

Troubleshooting Guide

Common issues and diagnostics:

  • Score remains zero for a new agent:

    • Ensure the agent is bonded; PendingBond agents cannot be scored.
    • Confirm the first epoch includes settled payments or ERC-8004 feedback.
  • Fee Activity does not grow:

    • Check that payment verification is configured and the asset matches.
    • Verify that transfers settle to the agent’s own address.
    • Confirm per-payer caps and fee unit settings.
  • Success Rate stays low despite good work:

    • Ensure attestations include an explicit success boolean and reach the oracle with a token. A request without one credits the payment and records no outcome (outcomeRecorded: false).
    • Accumulate enough observations; the prior makes small samples weak.
  • Tenure is unexpectedly low:

    • Tenure requires verified payments; idle registration time does not count.
    • Check recency; abandoned activity fades.
  • External Trust is zero:

    • Verify ERC-8004 feed configuration.
    • Confirm the ERC-8004 NFT is owned by the same wallet as the Sigvara operator.
  • Maturity feels too slow:

    • Review maturityRatePerDay.
    • Remember rises are gradual; falls are immediate.

Section sources

Conclusion

Sigvara’s six-factor model balances measurable economic activity, outcome quality, sustained operation, external reputation, community signals, and trusted counterparties. Decay and half-life keep scores current; per-payer caps and external-standing-only propagation resist Sybil and wash-trading attacks; maturity prevents farm-and-cash-out exploitation; and the bond requirement makes reputation accountable. The result is a score that favors agents with real, ongoing, slashable work from diverse counterparties and established external trust.

Overview

Clone this wiki locally