Agent Trust Oracle — Methodology (v1)

Read-only trust scores for ERC-8004 agents on Base mainnet. This page is the canonical reference for the scoring math; the values below are pulled live from scoring.py so they can never drift from the implementation.

Score range

0 to 100. Higher = more positive feedback from more independent clients, recently. Or null when data is too thin (see refusal threshold below).

Components & weights

Each component is computed independently on a 0–100 scale and then combined with the fixed weights below. Sum of weights = 1.00.

ComponentWeightWhat it measures
value_avg0.50Simple arithmetic mean of feedback values, after each value is normalised onto 0–100.
client_breadth0.20Log-scaled count of distinct client addresses that have left feedback.
volume0.15Log-scaled total count of (non-revoked) feedback entries.
recency0.15Weighted mean of feedback values, with older feedback decaying exponentially.

Refusal threshold (insufficient_data)

If the agent has fewer than 3 distinct clients leaving feedback, we return score = null with status = "insufficient_data — fewer than 3 distinct clients have left feedback". This prevents one client farming a high score by self-rating and is the single largest sybil-mitigation lever in v1.

Value normalisation (−100..100 → 0..100)

ERC-8004 feedback values are signed decimals with arbitrary scale (e.g. 99.77, 5.0, -3.0). We clamp to the inclusive range [-100, +100] then linearly remap to [0, 100]:

normalised_value = (clamp(value, -100, +100) + 100) / 2

Log-axis reference points

Distinct-client breadth and total feedback volume are mapped via log1p to the 0–100 range. Saturation references:

def log_axis(count, ref):
    if count <= 0: return 0.0
    return min(100, 100 * log1p(count) / log1p(ref))

Recency decay

Each feedback entry is weighted by an exponential decay on chain-block age:

recency_weight(block) = 0.5 ** ((latest_block - block) / 50000)

Half-life is 50,000 blocks (~27.8 hours at Base’s 2-second block time). Half of a year-old feedback is roughly worth 0.5 ** (315.4) ≈ vanishingly small.

Final aggregate

score = sum(component[k] * WEIGHTS[k] for k in WEIGHTS)
      = value_avg * 0.50
      + client_breadth * 0.20
      + volume * 0.15
      + recency * 0.15

What we do NOT do (yet)

v2 work-items would tighten the sybil model and add tag-conditional scoring. For now we prefer to refuse cleanly when data is thin (see threshold above).

Sources (read-only, Base mainnet)

Signatures. Every paid /agent-trust response and the free /agent-trust/preview response carry signed_by + signature. Verify with Ethereum personal_sign/ecrecover over json.dumps(payload, sort_keys=True, separators=(",",":")) with the two keys stripped — recover == signed_by ⇒ authentic & untampered. Signer: 0x5e63d01d6A266BC17f577B80199a2a07B15053C7.
Disclaimer. Read-only on-chain analytics. Informational only — not investment, employment, or counterparty advice. Score reflects observed feedback only; absence of evidence is not evidence of trustworthiness.