Repository navigation
Scoring Model and Formulas
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
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
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
Diagram sources
Section sources
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
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
Diagram sources
- payments.js:167-272
- external.js:171-180
- scoring.js:130-155
- SigvaraReputation.sol:378-448
- SigvaraReputation.sol:454-504
- SigvaraReputation.sol:696-752
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"])
Diagram sources
Section sources
- reputation-model.md:21-49
- payments.js:46-104
- payments.js:282-303
- payments.js:472-481
- payments.js:539-549
- payments.test.js:249-273
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"])
Diagram sources
Section sources
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
Diagram sources
Section sources
- reputation-model.md:82-131
- scoring.js:40-98
- payments.js:520-532
- scoring.test.js:63-83
- scoring.test.js:160-221
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
counteraudittag, 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"])
Diagram sources
Section sources
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 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"])
Diagram sources
Section sources
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"])
Diagram sources
Section sources
- reputation-model.md:21-49
- reputation-model.md:50-80
- reputation-model.md:82-131
- payments.js:282-303
- payments.js:331-346
- payments.js:520-532
- external.js:31-61
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
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
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
- reputation-model.md:21-49
- reputation-model.md:50-80
- reputation-model.md:82-131
- reputation-model.md:166-225
- scoring.test.js:118-151
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 prevents farm-and-cash-out attacks by releasing earned score gradually.
Key rules:
-
getEarnedScorereturns the raw sum of factors. -
getTotalScorereturns the matured value, which climbs toward the earned score atmaturityRatePerDay. - 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
getTotalScoreormeetsThreshold. - 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 --> [*]
Diagram sources
Section sources
- reputation-model.md:226-267
- reputation-model.md:285-312
- SigvaraReputation.sol:560-577
- SigvaraReputation.sol:696-752
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"]
Coupling and cohesion:
-
payments.jsis responsible for chain verification, decay, per-payer capping, and activity windows. -
external.jsis responsible for ERC-8004 normalization and ownership checks. -
scoring.jscomposes the six factors into a total score. -
SigvaraReputation.solenforces 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
- 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.
Common issues and diagnostics:
-
Score remains zero for a new agent:
- Ensure the agent is bonded;
PendingBondagents cannot be scored. - Confirm the first epoch includes settled payments or ERC-8004 feedback.
- Ensure the agent is bonded;
-
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.
- 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 (
-
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.
- Review
Section sources
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.
- 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