Skip to content

Custom Scoring Factors

RunTimeAdmin edited this page Nov 3, 2026 · 13 revisions

Custom Scoring Factors

**Referenced Files in This Document** - [scoring.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/scoring.js) - [external.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/external.js) - [jobs.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/jobs.js) - [store.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/store.js) - [epoch-policy.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/epoch-policy.js) - [index.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/index.js) - [SigvaraReputation.sol](https://github.com/RunTimeAdmin/Sigvara/blob/main/src/SigvaraReputation.sol) - [reputation-model.md](https://github.com/RunTimeAdmin/Sigvara/blob/main/docs/reputation-model.md) - [architecture.md](https://github.com/RunTimeAdmin/Sigvara/blob/main/docs/architecture.md) - [scoring.test.js](https://github.com/RunTimeAdmin/Sigvara/blob/main/oracle/scoring.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
  10. Appendices

Introduction

This document explains how to extend Sigvara’s reputation scoring system with custom factors. It covers the current six-factor architecture, where computation happens off-chain in the oracle and only finalized scores are stored on-chain, and how to add new scoring algorithms by modifying the oracle’s scoring module. It also documents evidence source integration patterns for connecting external data sources and validating incoming attestations, the mathematical foundations of score computation, weight distribution strategies, decay mechanisms, practical examples of custom factors such as social graph analysis or transaction pattern recognition, testing strategies, and performance considerations for real-time computation.

The key design principle is that the oracle computes a deterministic score from verifiable evidence, while the smart contract stores and enforces caps, challenge windows, maturity, and slashing behavior. New factors should be pure functions where possible, consume deterministic inputs, and integrate cleanly into the existing factor pipeline.

Project Structure

Sigvara separates scoring logic from storage:

  • The oracle implements evidence intake, normalization, decay, and factor computation.
  • The SigvaraReputation contract stores proposed and finalized scores, enforces per-factor caps, and exposes matured scores to consumers.
  • Documentation describes the model, signal topology, and governance around proposals and challenges.
graph TB
subgraph "Oracle"
SC["scoring.js<br/>factor functions"]
EX["external.js<br/>ERC-8004 feedback"]
JO["jobs.js<br/>ERC-8183 jobs"]
ST["store.js<br/>attestations, flags, payments"]
EP["epoch-policy.js<br/>action decisions"]
IN["index.js<br/>epoch orchestration"]
end
subgraph "On-chain"
REP["SigvaraReputation.sol<br/>proposal, finalize, maturity"]
end
SC --> IN
EX --> IN
JO --> IN
ST --> IN
EP --> IN
IN --> REP
Loading

Diagram sources

Section sources

Core Components

The current scoring system has six factors:

  • Fee Activity: measures verified payment volume, capped at 20 points.
  • Success Rate: measures outcomes signed by the payer or recorded by a credentialed service against total outcome-bearing attestations, capped at 15 points.
  • Tenure: measures span of verified paid activity faded by recency, capped at 30 points.
  • External Trust: normalizes ERC-8004 feedback, capped at 25 points.
  • Community: penalizes unresolved flags, capped at 5 points.
  • Propagation: inherits trust from counterparties’ external standing, capped at 5 points.

These factors are computed off-chain and summed to a maximum of 100. The contract validates each factor against its cap before accepting a proposal.

The oracle keeps the six caps in one frozen table, oracle/score-caps.js, which scoring.js, payments.js and external.js all read. score-caps.test.js compares each cap with the matching MAX_*_SCORE constant in SigvaraReputation.sol, so a reweight is one edit there, plus a failing test if the contract was not changed with it.

Section sources

Architecture Overview

The scoring flow is:

  1. Evidence collection: attestations, flags, payment events, ERC-8004 feedback, and optional ERC-8183 job verdicts.
  2. Normalization and decay: convert raw signals into weighted values using shared decay functions.
  3. Factor computation: compute each factor as a pure function.
  4. Aggregation: sum factors and clamp totals.
  5. Proposal: the oracle proposes a score plus an evidence root to the contract.
  6. Challenge window: the community or committee can reject a bad proposal.
  7. Finalization: after the window, anyone can finalize; the score becomes spendable over time via maturity.
sequenceDiagram
participant Source as "Evidence Sources"
participant Oracle as "Oracle index.js"
participant Store as "store.js"
participant Scoring as "scoring.js"
participant Contract as "SigvaraReputation.sol"
Source->>Store : Attestations, flags, payments
Oracle->>Store : Load state and scan chain
Oracle->>Scoring : Compute factors
Scoring-->>Oracle : {feeScore, successScore, ageScore, externalScore, communityScore, propagationScore}
Oracle->>Contract : proposeReputation(data, evidenceRoot)
Contract-->>Oracle : PendingScore opened
Note over Contract : Challenge window opens
Oracle->>Contract : finalizeReputation(didHash)
Contract-->>Oracle : ReputationUpdated(totalScore)
Loading

Diagram sources

Detailed Component Analysis

Scoring Module: Pure Factor Functions

The scoring module defines pure functions for each factor and a central aggregator. This makes factors testable in isolation and ensures determinism across operators.

Key implementation patterns:

  • Each factor returns a bounded integer score.
  • Time-dependent factors accept explicit clocks to avoid host drift.
  • Decay is applied upstream so factor functions receive already-weighted inputs.
  • The aggregator sums factors and clamps external and propagation scores to their caps.
flowchart TD
Start(["computeScore"]) --> Fee["Compute feeScore"]
Start --> Success["Compute successScore"]
Start --> Age["Compute ageScore"]
Start --> External["Clamp externalScore"]
Start --> Community["Compute communityScore"]
Start --> Propagation["Clamp propagationScore"]
Fee --> Sum["Sum factors"]
Success --> Sum
Age --> Sum
External --> Sum
Community --> Sum
Propagation --> Sum
Sum --> Return(["Return {factors, total}"])
Loading

Diagram sources

Section sources

Evidence Source Integration Patterns

ERC-8004 External Feedback

External trust is normalized from recognized tags in the ERC-8004 registry, counted only from reviewers that pass reviewers.js:3-51: bonded Sigvara agents registered for 30 days, with a matured score of at least 40, that paid the agent, one vote per cluster of reviewers sharing an operator or a funder. The module verifies ownership of the linked identity. CounterAudit feedback uses its own tag, which is not recognised, so it is not counted twice.

Integration steps:

  • Configure external RPC and contracts.
  • Verify NFT ownership matches expected operator wallet.
  • Read clients, last indices, and feedback rows.
  • Normalize recognized tags to a rating from 0 to 1, rate each qualifying reviewer by its mean, and weight independent reviewers by how recently each paid.
  • Scale mean to the external score cap.
flowchart TD
Init["init(cfg)"] --> Enabled{"Enabled?"}
Enabled --> |No| Zero["externalScore = 0"]
Enabled --> |Yes| Verify["verifyOwnership(agentId, owner)"]
Verify --> Valid{"Owner matches?"}
Valid --> |No| Zero
Valid --> |Yes| Read["readFeedbackRows(agentId)"]
Read --> Normalize["normalize recognized tags"]
Normalize --> Mean["reviewers.scoreFeedback: qualifying reviewers, weighted mean, at most MAX_EXTERNAL_SCORE"]
Mean --> Result["externalScoreFor returns score"]
Loading

Diagram sources

Section sources

ERC-8183 Job Verdicts

Job verdicts provide on-chain success/failure signals without self-reporting. The module decodes events, assembles job lifecycles, filters invalid cases, and converts jobs into evidence compatible with the existing merkle leaf format.

Integration steps:

  • Enable via registry address and RPC.
  • Scan logs for job events.
  • Decode and assemble jobs.
  • Filter out self-paid, self-evaluated, non-terminal, or missing-settlement jobs.
  • Convert to evidence shape with payer, amount, settledAt, and success.
flowchart TD
Scan["scanLogs(registry, blocks)"] --> Decode["decodeLog(log)"]
Decode --> Assemble["assembleJobs(decoded, blockTimes)"]
Assemble --> Filter["rejectionReason(job)"]
Filter --> Keep{"Valid evidence?"}
Keep --> |Yes| ToEvidence["toEvidence(jobs, provider)"]
Keep --> |No| Drop["record dropped reason"]
ToEvidence --> Output["evidence array"]
Loading

Diagram sources

Section sources

Attestations, Flags, and Payment Events

The store persists attestation outcomes, flags with timestamps, links, cooldowns, and payment events. It provides decayed flag counts, deduplication of settlement transactions, and pruning of old events.

Integration steps:

  • Validate and record attestations with cooldowns.
  • Record flags with timestamps and decay weights.
  • Credit payment events with txHash, amount, payer, and outcome. An outcome is recorded only for a caller holding an oracle token or from the payer's own signed outcome; a stranger's attestation, like a payment found by the chain scan, credits the payment with no outcome, and either can add one later, once.
  • Prune old events and flags based on half-life thresholds.
flowchart TD
Input["Attestation / Flag / Payment"] --> Validate["Validate fields"]
Validate --> Dedupe{"Already credited?"}
Dedupe --> |Yes| Reject["Reject duplicate"]
Dedupe --> |No| Persist["Persist to store"]
Persist --> Decay["Apply decay weights"]
Decay --> ScoreInput["Provide decayed inputs to scoring"]
Loading

Diagram sources

Section sources

Mathematical Foundations

Success Rate with Pseudo-Observations

Success rate uses pseudo-observations in the denominator to prevent scale invariance and single-observation dominance. The formula approaches but never reaches the cap, ensuring evidence accumulation matters.

Formula characteristics:

  • Denominator includes a prior constant.
  • Score increases with both success ratio and total observations.
  • Decayed weights reduce both numerator and denominator, causing silence to cost points.

Section sources

Tenure Curve and Recency

Tenure measures the span between first and last verified payment, not calendar age since registration. It applies a logarithmic curve and multiplies by recency to fade abandoned agents.

Curve characteristics:

  • Logarithmic growth over days.
  • Capped amplitude.
  • Recency clamped to 0–1.
  • With payment verification off there is no activity data, and it falls back to calendar age for legacy deployments. With verification on, an agent with no payments gets an empty window and scores 0.

Section sources

Weight Distribution Strategy

Weights are distributed to resist wash trading and Sybil attacks:

  • Fee and success were reduced to limit easily manufactured activity.
  • Tenure and external trust increased because they cannot be shortcut.
  • Community and propagation remain small but meaningful.

Caps ensure the total does not exceed 100 and each factor is validated on-chain.

Section sources

Decay Mechanisms

Decay is applied uniformly across signals:

  • Payments decay by half-life.
  • Flags decay faster than payments.
  • Decayed weights feed into volume, success ratio, tenure recency, and community penalties.

Shared decay functions ensure consistency across components.

Section sources

Implementing Custom Scoring Factors

Step-by-Step Extension Pattern

To add a custom factor:

  1. Define a pure function that takes deterministic inputs and returns a bounded score.
  2. Integrate evidence collection if needed (e.g., external API, chain logs).
  3. Normalize and validate inputs.
  4. Apply decay if appropriate.
  5. Add the factor to the aggregation step in computeScore.
  6. Update the contract’s ReputationData struct and caps if adding a new slot, and the matching entry in oracle/score-caps.js.
  7. Add tests covering edge cases, bounds, and determinism.
classDiagram
class CustomFactor {
+compute(input) number
+validate(input) bool
+decay(value, timestamp) number
}
class ScoringModule {
+computeScore(options) object
+feeScore()
+successScore()
+ageScore()
+communityScore()
+customFactor()
}
class EvidenceSource {
+fetchData() array
+normalize(rows) array
+verifyOwnership() bool
}
CustomFactor --> EvidenceSource : "consumes"
ScoringModule --> CustomFactor : "calls"
Loading

Diagram sources

Section sources

Practical Examples

Social Graph Analysis

A social graph factor could measure how many distinct counterparties have interacted with an agent, weighted by their own external standing. Inputs include counterparty addresses, their ERC-8004 external scores, and interaction frequency.

Implementation notes:

  • Use unique counterparty count rather than volume to avoid amplification.
  • Cap contribution per counterparty.
  • Apply decay to older interactions.
  • Ensure no reflexivity: do not inherit from the agent itself.
Transaction Pattern Recognition

A pattern recognition factor could detect consistent behavior such as regular settlement intervals, diverse asset usage, or low volatility in trade sizes. Inputs include payment timestamps, amounts, assets, and counterparties.

Implementation notes:

  • Use statistical measures like variance, autocorrelation, or entropy.
  • Normalize to 0–1 range.
  • Apply decay to recent anomalies differently than long-term trends.
  • Guard against gaming by requiring minimum observation windows.
Domain-Specific Trust Signals

Domain-specific signals might include compliance checks, regulatory status, or industry certifications. Inputs come from external registries or attestations.

Implementation notes:

  • Treat signals as binary or graded trust indicators.
  • Require verification of source authenticity.
  • Exclude self-reported signals unless cryptographically backed.
  • Clamp scores to avoid dominating other factors.

Section sources

Testing Strategies

Unit Tests for Pure Factors

Each factor should have unit tests covering:

  • Boundary conditions (zero, negative, overflow).
  • Monotonicity (inputs increase → score does not decrease).
  • Caps and floors.
  • Decay behavior.
  • Determinism with explicit clocks.

Existing tests demonstrate these patterns for fee, success, age, community, and aggregation.

Section sources

Integration Tests for Evidence Sources

Test external feedback normalization, ownership verification, and error handling. Test job decoding, assembly, filtering, and conversion to evidence.

Section sources

On-Chain Validation Tests

Verify that proposals with out-of-range factors revert, challenge windows work, finalization updates matured scores, and slashing zeroes everything.

Section sources

Performance Considerations

Real-Time Computation

  • Prefer pure functions for factors to enable caching and parallel evaluation.
  • Chunk external reads and job scans to respect RPC limits.
  • Use shared decay functions to avoid duplication.
  • Prune old events and flags to keep state size bounded.

Operator Agreement

  • Thread chain timestamps through scoring to avoid host clock drift.
  • Use deterministic inputs for all time-dependent calculations.
  • Maintain divergence tolerance for float drift while aiming for fixed-point arithmetic in the future.

Section sources

Dependency Analysis

graph LR
Index["index.js"] --> Scoring["scoring.js"]
Index --> Store["store.js"]
Index --> External["external.js"]
Index --> Jobs["jobs.js"]
Index --> EpochPolicy["epoch-policy.js"]
External --> Index
Jobs --> Index
Store --> Index
EpochPolicy --> Index
Index --> Contract["SigvaraReputation.sol"]
Loading

Diagram sources

Section sources

Performance Considerations

  • Keep factor functions pure and side-effect-free.
  • Cache normalized external feedback when safe.
  • Use chunked I/O for chain scans.
  • Prune old state to prevent unbounded growth.
  • Measure latency of external RPC calls and set timeouts.
  • Avoid heavy computations inside hot paths; precompute where possible.

Troubleshooting Guide

Common issues and resolutions:

  • External feed unreachable: check RPC configuration and chain ID resolution.
  • Ownership mismatch: verify NFT owner matches expected operator wallet.
  • Duplicate attestations: ensure settlement hashes are deduplicated per recipient.
  • Divergence between operators: inspect per-factor deltas and evidence roots.
  • State persistence failures: monitor health endpoint and file system permissions.

Section sources

Conclusion

Extending Sigvara’s reputation scoring system requires careful integration of evidence sources, pure factor functions, and robust testing. The current architecture supports adding custom factors while maintaining determinism, decay, and on-chain validation. By following the extension patterns and considering performance and security implications, you can implement domain-specific trust signals, social graph analysis, or transaction pattern recognition without compromising the integrity of the scoring pipeline.

Appendices

Appendix A: Factor Summary Table

Factor Max Points Source Formula Notes
Fee Activity 20 Verified payment volume Decayed volume per payer, capped
Success Rate 15 Outcomes signed by the payer or reported by a credentialed service Ratio with pseudo-observations
Tenure 30 Span of verified activity Logarithmic curve × recency
External Trust 25 ERC-8004 feedback Normalized tag mean scaled to cap
Community 5 Unresolved flags Linear penalty with decay
Propagation 5 Counterparty external standing Pro-rated by counterparty external standing

Section sources

Overview

Clone this wiki locally