Repository navigation
Scoring Engine
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
This document explains Sigvara’s multi-factor reputation scoring engine. The system computes a deterministic score from 0 to 100 using six factors: fee activity, success rate, tenure, external trust, community signals, and propagation metrics. The oracle network performs the computation off-chain; the on-chain contract stores and serves finalized scores.
The design emphasizes anti-sybil resilience, decayed historical data, gradual maturity, and clear configuration trade-offs. It is intended for both technical readers who need implementation details and non-technical readers who need conceptual clarity.
The scoring engine spans documentation, an off-chain oracle, and an on-chain reputation store:
- Documentation defines the model, weights, and security rationale.
- The oracle reads chain events, verifies payments, normalizes external feedback, aggregates attestations, and proposes scores.
- The on-chain contract validates factor caps, enforces bonding, manages proposals, finalization, maturity, and consumption APIs.
graph TB
Docs["docs/reputation-model.md"]
OracleIndex["oracle/index.js"]
Payments["oracle/payments.js"]
Scoring["oracle/scoring.js"]
External["oracle/external.js"]
Contract["src/SigvaraReputation.sol"]
Docs --> OracleIndex
OracleIndex --> Payments
OracleIndex --> Scoring
OracleIndex --> External
OracleIndex --> Contract
Diagram sources
- reputation-model.md:1-20
- index.js:1-142
- payments.js:42-104
- scoring.js:130-155
- external.js:1-21
- SigvaraReputation.sol:14-75
Section sources
The scoring engine consists of:
- Six-factor model with explicit maximums summing to 100.
- Decay mechanisms that age volume, attestations, flags, and recency.
- Anti-sybil controls including per-payer diversity caps, self-payment detection, and counterparty standing evaluation.
- Maturity that gradually releases earned scores over time.
- Oracle epoching that scans chain state, computes scores, and proposes them through a challenge window.
- On-chain storage that enforces bonding, caps, proposal lifecycle, and maturity.
Key responsibilities:
-
oracle/scoring.js: Pure functions for each factor and total score composition. -
oracle/score-caps.js: The six factor caps in one frozen table, read byscoring.js,payments.jsandexternal.js. A test compares each with the matchingMAX_*_SCOREconstant inSigvaraReputation.sol, so a reweight is one edit plus a failing test if the contract was not changed with it. -
oracle/payments.js: Payment verification, decay, diversification, and propagation helpers. -
oracle/external.js: ERC-8004 feedback normalization and ownership checks. -
oracle/index.js: Epoch orchestration, environment configuration, and evidence handling. -
src/SigvaraReputation.sol: Score storage, proposal/finalization, maturity, and threshold queries.
Section sources
- scoring.js:130-155
- payments.js:42-104
- external.js:1-21
- index.js:92-142
- SigvaraReputation.sol:110-135
The end-to-end flow is:
- The oracle scans registered agents and payment events.
- It verifies settlement transactions and extracts payers.
- It decays historical data by half-life.
- It applies diversity caps and self-payment detection.
- It computes the six factors.
- It proposes a score on-chain during the oracle epoch.
- After the challenge window, anyone can finalize the score; on main the contract refuses a proposal whose proposer has since lost its authority.
- Consumers read the matured score for gating decisions.
sequenceDiagram
participant Chain as "Blockchain"
participant Oracle as "Oracle index.js"
participant Payments as "payments.js"
participant Scoring as "scoring.js"
participant External as "external.js"
participant Contract as "SigvaraReputation.sol"
Chain-->>Oracle : "Agent list + events"
Oracle->>Payments : "verifyPayment / scan transfers"
Payments-->>Oracle : "Payers, amounts, settledAt"
Oracle->>Payments : "decayWeight / diversifiedVolume / activityWindow"
Oracle->>External : "readFeedbackRows + verifyOwnership"
External-->>Oracle : "externalScore"
Oracle->>Scoring : "computeScore(...)"
Scoring-->>Oracle : "fee, success, age, external, community, propagation, total"
Oracle->>Contract : "proposeReputation(data, evidenceRoot)"
Note over Contract : "Challenge window opens"
Oracle->>Contract : "finalizeReputation(didHash)"
Contract-->>Consumers : "getTotalScore / meetsThreshold"
Diagram sources
- index.js:295-322
- index.js:925-944
- payments.js:167-272
- payments.js:282-303
- payments.js:472-481
- external.js:176-202
- scoring.js:130-155
- SigvaraReputation.sol:378-448
- SigvaraReputation.sol:454-504
- SigvaraReputation.sol:696-752
The score is composed of six capped factors totaling 100:
| Factor | Maximum Points | Source | Formula Summary |
|---|---|---|---|
| Fee Activity | 20 | Settled payments verified on-chain |
min(20, decayedVolume / PAYMENT_FEE_UNIT), capped per payer |
| Success Rate | 15 | Outcome signed by the payer (positive only for now), or reported by a credentialed service |
floor(successful / (total + 5) × 15) on decayed weights |
| Tenure | 30 | Span between first and last verified payment, faded by recency | min(30, floor(log₂(spanDays+1) × 3)) × recency |
| External Trust | 25 | Normalized ERC-8004 feedback for a linked agent | Recency-weighted mean of qualifying reviewers' ratings, scaled to 25, in full only with five independent reviewers that paid recently |
| Community Signals | 5 | Unresolved flags | max(0, 5 − flags × 2) |
| Propagation Metrics | 5 | Standing of counterparties that paid | One point per fully trusted counterparty, pro-rated by its external standing |
These formulas are implemented in the oracle and documented in the model specification.
flowchart TD
Start(["Compute Score"]) --> Fee["Fee Activity<br/>decayedVolume / feeUnit"]
Start --> Success["Success Rate<br/>successful / (total + prior) × 15"]
Start --> Tenure["Tenure<br/>log2(spanDays+1) × 3 × recency"]
Start --> External["External Trust<br/>qualifying reviewers' weighted mean × 25"]
Start --> Community["Community Signals<br/>max(0, 5 - flags × 2)"]
Start --> Propagation["Propagation Metrics<br/>sum of counterparty external standing"]
Fee --> Sum["Sum all six factors"]
Success --> Sum
Tenure --> Sum
External --> Sum
Community --> Sum
Propagation --> Sum
Sum --> Total["Total score ≤ 100"]
Diagram sources
- reputation-model.md:7-16
- scoring.js:11-19
- scoring.js:35-38
- scoring.js:57-60
- scoring.js:90-98
- scoring.js:100-110
- scoring.js:130-155
- payments.js:545-549
- payments.js:419-430
Section sources
- reputation-model.md:7-16
- scoring.js:11-19
- scoring.js:35-38
- scoring.js:57-60
- scoring.js:90-98
- scoring.js:100-110
- scoring.js:130-155
- payments.js:545-549
- payments.js:419-430
Fee activity measures real economic activity rather than request counts. When payment verification is enabled, the oracle uses measured settlement volume; otherwise it falls back to an attestation-count proxy.
Important mechanics:
- Volume is age-weighted using a configurable half-life.
- Each counterparty contributes at most
PAYMENT_MAX_PER_PAYERpoints of fee score. - A trusted counterparty’s cap can be raised proportionally to its external standing.
- Self-payments are refused at intake and never enter the payment log.
flowchart TD
Events["Payment Events"] --> Verify["Verify Settlement Transaction"]
Verify --> Payers["Extract Payers and Amounts"]
Payers --> Decay["Apply Half-Life Decay"]
Decay --> Cap["Cap Per-Payer Contribution"]
Cap --> Volume["Compute Decayed Volume"]
Volume --> FeePoints["feeUnit → Points"]
FeePoints --> FeeScore["Clamp to MAX_FEE_SCORE"]
Diagram sources
Section sources
- payments.js:42-104
- payments.js:167-272
- payments.js:282-303
- payments.js:472-481
- payments.js:545-549
- scoring.js:11-19
Success rate measures outcome quality while penalizing small samples and stale records.
Key properties:
- Uses a Bayesian-style prior of five pseudo-observations.
- Divides successful outcomes by
total + prior. - Both numerator and denominator are age-weighted so old success streaks cannot mask recent failures.
- The full 15 points is approached but not reached due to the prior.
- Only outcomes the payer signed, or that a caller holding an oracle token recorded, count. A stranger's attestation, like a payment found by the chain scan, credits the payment with no outcome and stays out of both sides of the ratio.
flowchart TD
Attestations["Outcome-Bearing Attestations"] --> Weight["Age-Weight Successful and Total"]
Weight --> Prior["Add SUCCESS_PRIOR = 5 to Denominator"]
Prior --> Ratio["Ratio = successful / (total + prior)"]
Ratio --> Scale["Scale by MAX_SUCCESS_SCORE = 15"]
Scale --> Floor["Floor to Integer"]
Diagram sources
Section sources
Tenure measures sustained paid activity rather than calendar age since registration.
Key properties:
- Uses the span between first and last verified payment.
- Applies a logarithmic curve:
floor(log₂(spanDays+1) × 3). - Multiplies by recency based on how recently the last payment occurred.
- If payment verification is disabled, it falls back to calendar age since registration. With verification on, an agent with no verified payments scores 0.
flowchart TD
Activity["First and Last Verified Payment"] --> Span["Span Days = last - first"]
Span --> Curve["Logarithmic Curve: min(30, floor(log₂(spanDays+1) × 3))"]
Activity --> Recency["Recency from Most Recent Payment"]
Curve --> Tenure["Tenure = Curve × Recency"]
Diagram sources
Section sources
External trust imports cross-protocol reputation from ERC-8004.
Key properties:
- Only active when external RPC, identity, and reputation addresses are configured.
- Verifies that the ERC-8004 NFT owner matches the Sigvara operator.
- Normalizes recognized tags into a rating from 0 to 1, counts only reviewers that pass
reviewers.js, and scales the weighted mean to 25 points, in full only with five independent reviewers. - Reads only the nine recognised tags; CounterAudit publishes under its own tag, so its feedback is not counted twice.
flowchart TD
Link["Linked ERC-8004 Identity"] --> Ownership["Verify Owner Matches Operator"]
Ownership --> Feedback["Read Feedback Rows"]
Feedback --> Normalize["Normalize Recognized Tags to 0–1"]
Normalize --> Mean["Keep Qualifying Reviewers, Weighted Mean over Clusters"]
Mean --> Scale["Scale by min(1, weight / 5) to MAX_EXTERNAL_SCORE = 25"]
Diagram sources
Section sources
Community signals penalize unresolved flags.
Key properties:
- Starts at 5 points for unflagged agents.
- Deducts two points per flag, floored at zero.
- Flags are age-weighted so they decay rather than permanently punishing an agent.
flowchart TD
Flags["Unresolved Flags"] --> Weight["Age-Weighted Flag Count"]
Weight --> Penalty["Penalty = flags × 2"]
Penalty --> Clamp["Clamp to Zero Minimum"]
Clamp --> Community["Community Score = max(0, floor(5 - penalty))"]
Diagram sources
Section sources
Propagation metrics measure the breadth of trusted counterparties.
Key properties:
- One point per counterparty that has external standing, pro-rated by that counterparty’s external score.
- Each counterparty counts once regardless of payment size.
- Uses matured external standing to prevent reflexivity and bootstrapping.
- 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.
flowchart TD
Payers["Distinct Payers"] --> Standing["Counterparty External Standing"]
Standing --> ProRate["Pro-Rate by External Score"]
ProRate --> Sum["Sum Across Distinct Payers"]
Sum --> Cap["Cap at MAX_PROPAGATION_SCORE = 5"]
Diagram sources
Section sources
Decay prevents farm-and-cash-out behavior by aging historical data.
Mechanics:
- Half-life decay uses
0.5 ^ (ageMs / halfLifeMs). - Volume, attestations, flags, and recency are all age-weighted.
- A half-life of zero disables decay.
- Negative ages from clock skew are treated as current weight.
flowchart TD
Age["Age in Milliseconds"] --> HalfLife["Half-Life Configuration"]
HalfLife --> Weight["Weight = 0.5 ^ (ageMs / halfLifeMs)"]
Weight --> Scale["Scaled by WEIGHT_SCALE"]
Scale --> Apply["Apply to Volume, Attestations, Flags, Recency"]
Diagram sources
Section sources
Maturity gradually makes earned scores spendable.
Key properties:
- Earned score is computed immediately.
- Matured score climbs toward earned score at a configurable points-per-day rate.
- Falls are immediate; rises are delayed.
- Operator transfer restarts maturity for the buyer.
stateDiagram-v2
[*] --> Earned
Earned --> Matured : "Time passes at maturityRatePerDay"
Matured --> Earned : "Earned score falls"
Matured --> Matured : "Earned score rises slowly"
Earned --> Restart : "Operator transfer"
Matured --> Restart : "Operator transfer"
Restart --> Earned : "Anchor resets"
Diagram sources
Section sources
Anti-sybil protections include diversity caps, self-payment detection, and counterparty standing evaluation.
Key controls:
-
Diversity caps:
PAYMENT_MAX_PER_PAYERlimits how much one counterparty can contribute to fee activity and success evidence. - Self-payment detection: Payments from the operator or agent address are rejected.
- Counterparty standing evaluation: Trusted counterparties receive higher caps proportional to their external standing; unknown wallets get no boost.
- Bond requirement: Only bonded agents are scored.
- Slash reset: Slashing zeroes all factors and marks the agent terminal.
flowchart TD
Payer["Counterparty"] --> SelfCheck{"Is Self-Payment?"}
SelfCheck --> |Yes| Reject["Reject Payment Evidence"]
SelfCheck --> |No| Diversity["Apply Per-Payer Diversity Cap"]
Diversity --> Standing["Adjust Cap by External Standing"]
Standing --> BondCheck{"Agent Bonded?"}
BondCheck --> |No| Skip["Skip Scoring"]
BondCheck --> |Yes| Score["Include in Score Factors"]
Diagram sources
- payments.js:348-361
- payments.js:402-406
- payments.js:472-481
- SigvaraReputation.sol:620-631
- SigvaraReputation.sol:541-557
Section sources
- payments.js:348-361
- payments.js:402-406
- payments.js:472-481
- SigvaraReputation.sol:620-631
- SigvaraReputation.sol:541-557
Below are representative scenarios. These illustrate how the model behaves; they are not test fixtures.
- Fee Activity: 0 because no settled payments exist.
- Success Rate: 0 because there are no attestations.
- Tenure: 0 because there is no span of verified payments.
- External Trust: 0 unless the agent links an ERC-8004 identity with existing feedback.
- Community Signals: 5 because there are no unresolved flags.
- Propagation Metrics: 0 because there are no counterparties.
- Total: 5 points, which is also the starting community baseline.
This scenario reflects the new-agent ramp-up described in the model documentation.
Section sources
- Fee Activity: Approaches 20 as decayed volume reaches multiple
PAYMENT_FEE_UNITthresholds across distinct payers. - Success Rate: Approaches 15 as successful outcomes accumulate against the prior.
- Tenure: Approaches 30 as the span between first and last payment grows and recency remains high.
- External Trust: Depends on normalized ERC-8004 feedback.
- Community Signals: Remains near 5 if flags are low.
- Propagation Metrics: Increases as counterparties have external standing.
- Total: Can approach 100 only with sustained, diverse, and externally trusted activity.
Section sources
- reputation-model.md:21-49
- reputation-model.md:50-91
- reputation-model.md:93-141
- reputation-model.md:143-191
- reputation-model.md:208-266
- Diversity caps limit each ring member’s contribution.
- Self-payment detection rejects operator-to-agent flows.
- Propagation uses external standing, not total score, so a farmed total cannot inflate inherited trust.
- Decay ensures farmed volume must be renewed.
- Bond requirement prevents scoring unbonded identities.
- Slash reset destroys reputation after malicious behavior.
This scenario illustrates why wash trading alone cannot reliably reach high scores.
Section sources
- payments.js:348-361
- payments.js:402-406
- payments.js:432-444
- payments.js:472-481
- SigvaraReputation.sol:620-631
- SigvaraReputation.sol:541-557
The scoring engine depends on several modules and contracts:
-
oracle/index.jsorchestrates epochs, reads configuration, and coordinates scoring. -
oracle/payments.jsprovides payment verification, decay, diversification, and propagation. -
oracle/scoring.jsimplements pure factor functions and total composition. -
oracle/external.jsintegrates ERC-8004 feedback. -
src/SigvaraReputation.solstores scores, enforces proposal lifecycle, and exposes maturity-aware reads.
graph LR
Index["index.js"] --> Payments["payments.js"]
Index --> Scoring["scoring.js"]
Index --> External["external.js"]
Index --> Contract["SigvaraReputation.sol"]
Payments --> Scoring
External --> Scoring
Diagram sources
Section sources
The oracle is designed for bounded concurrency and RPC efficiency:
- Epoch reads are chunked to avoid overwhelming public RPC endpoints.
- Batched JSON-RPC calls reduce round trips.
- Payer standing is cached per epoch.
- Decayed arithmetic uses integer scaling to preserve precision.
- The checker mode recomputes independently and compares without overwriting primary proposals.
Trade-offs:
- Chunk size balances throughput against provider rate limits.
- Concurrency reduces latency but increases burst pressure.
- Float drift tolerance allows minor differences between operators while preserving auditability.
Section sources
Common issues and diagnostics:
- Missing configuration: Required environment variables such as RPC URL, private key, identity address, and reputation address must be set.
-
Payment verification disabled: With
PAYMENT_VERIFICATIONoff (the default), fee activity falls back to an attestation-count proxy, tenure to the calendar curve, and propagation is 0. Setting it torequiredwithoutPAYMENT_ASSET, or withPAYMENT_MIN_AMOUNTat 0, stops the process at startup. - RPC throttling: Epoch operations may be rate-limited; the oracle retries and logs failures without killing the process.
- Divergence between operators: Checker mode records divergence details, including evidence roots and factor differences.
- Unbonded agent: Scoring skips agents below minimum stake.
- Slashed agent: All factors are zeroed and the agent becomes terminal.
Recommended steps:
- Validate environment variables and endpoint connectivity.
- Confirm payment verification settings match the intended asset and units.
- Check whether the agent is bonded and not slashed.
- Review oracle logs for epoch failures, payment scan errors, and divergence records.
- Use on-chain view functions to inspect earned and matured scores.
Section sources
- index.js:158-198
- index.js:483-496
- index.js:946-1088
- SigvaraReputation.sol:620-631
- SigvaraReputation.sol:541-557
Sigvara’s scoring engine combines six complementary factors to produce a robust, decayed, and matured reputation score. Its security model emphasizes real economic activity, external trust, diversity caps, self-payment rejection, and gradual maturity. The oracle’s epoching and the contract’s proposal lifecycle provide auditable, slashable reputation with clear configuration trade-offs.
For consumers, the recommended read is the matured score via getTotalScore, and threshold checks should use meetsThreshold. For operators, the most important levers are payment verification configuration, diversity caps, external trust linkage, and maintaining bonded status.
The following parameters influence scoring behavior and security trade-offs:
| Parameter | Default Behavior | Effect | Trade-off |
|---|---|---|---|
PAYMENT_VERIFICATION |
Off or required | Enables on-chain payment verification | Required mode is more secure but needs a valid asset and RPC access |
PAYMENT_ASSET |
None unless required | Defines the accepted ERC-20 asset | Must match settlement tokens; a missing value with verification required stops the process at startup |
PAYMENT_MIN_AMOUNT |
Configurable minimum, applied to each sender; above 0 when verification is required | Filters out tiny payments; a zero-value transfer never counts | Too high excludes legitimate microtransactions |
PAYMENT_MIN_CONFIRMATIONS |
Configurable confirmation depth | Prevents scoring reorged transactions | Higher values increase latency |
PAYMENT_FEE_UNIT |
Scaled base units per point | Controls fee activity sensitivity | Lower unit saturates faster; higher unit requires more volume |
PAYMENT_HALF_LIFE_DAYS |
90 days | Controls decay speed | Shorter half-life favors recent activity; longer half-life rewards history |
PAYMENT_MAX_PER_PAYER |
4 points per payer, so five payers with no standing of their own fill the fee factor; 0 disables the cap | Limits Sybil concentration | Lower cap increases diversity requirements |
PAYMENT_TRUST_WEIGHT |
Configurable multiplier | Raises caps for trusted counterparties | Higher weight amplifies external standing but risks collusion if external trust is weak |
EPOCH_HOURS |
24 hours | Oracle epoch interval | Shorter intervals improve freshness but increase RPC load |
LOG_CHUNK_SIZE |
2000 blocks | Event scan chunk size | Larger chunks improve throughput but risk RPC limits |
DIVERGENCE_TOLERANCE |
Configurable tolerance | Allows minor float drift between operators | Higher tolerance hides disagreements; lower tolerance demands fixed-point arithmetic |
maturityRatePerDay |
Configurable points per day, at most 100 on main | Controls maturity ramp | Higher rate reduces farm-and-cash-out window but may feel less responsive |
challengeWindow |
Configurable seconds, from 1 hour to 7 days on main | Time before proposal finalization | Longer window improves dispute opportunity but delays score updates |
Section sources
- Agent Registration and Lifecycle
- Contract Overview and Architecture
- Economic Security Model and Incentives
- Evidence Verification and Merkle Proofs
- Key Management and Rotation
- Maturity Mechanism and Score Release
- Proposal Workflow and Challenge System
- Score Storage and Data Model
- SigvaraIdentity Contract
- SigvaraOracleBond Contract
- SigvaraReputation Contract
- SigvaraStaking Contract
- Slashing Lifecycle and Dispute Resolution
- Stake Management Functions
- Status Management and Control
- Token and Fee Contracts
- Unbonding Period and Withdrawal Security
- Anti-Manipulation and Security Measures
- Community Flag Processing
- Evidence Sources and Verification
- External Trust Integration
- Merkle Proof System
- Oracle Score Computation
- Payment-Backed Attestations
- Scoring Model and Formulas
- Agent Creation and Key Management
- Agent Operations
- Agent Registration Workflow
- Challenge-Response Authentication
- Gate Access Control
- Integration Patterns and Examples
- SDK Overview and Setup
- Verifier Functions