ERC-8004 Reputation
On-chain reputation for AI agents. Give feedback, check scores, view leaderboards, and build trust via the ERC-8004 Reputation Registry. Supports Base, Ethereum, Polygon, Monad, BNB.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-erc8004-reputation
Repository
Skill path: skills/aetherstacey/erc8004-reputation
On-chain reputation for AI agents. Give feedback, check scores, view leaderboards, and build trust via the ERC-8004 Reputation Registry. Supports Base, Ethereum, Polygon, Monad, BNB.
Open repositoryBest for
Primary workflow: Analyze Data & AI.
Technical facets: Full Stack, Data / AI.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install ERC-8004 Reputation into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding ERC-8004 Reputation to shared team environments
- Use ERC-8004 Reputation for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: ERC-8004 Reputation
description: On-chain reputation for AI agents. Give feedback, check scores, view leaderboards, and build trust via the ERC-8004 Reputation Registry. Supports Base, Ethereum, Polygon, Monad, BNB.
---
# ERC-8004 Reputation Skill
Interact with the ERC-8004 Reputation Registry — the decentralized reputation layer for AI agents.
## Use This When...
- "Check an agent's reputation"
- "Rate this agent"
- "Give feedback to agent X"
- "What's my agent's reputation?"
- "Who gave feedback to my agent?"
- "Show me the reputation leaderboard"
- "Top agents by reputation"
- "Revoke my feedback"
## Commands
### lookup
Look up an agent's reputation summary.
```bash
python scripts/reputation.py lookup <agentId> [--chain CHAIN]
```
Shows: reviewer count, feedback count, summary value, individual feedback.
### give
Give feedback to an agent.
```bash
python scripts/reputation.py give <agentId> <value> [--decimals N] [--tag1 TAG] [--tag2 TAG] [--chain CHAIN]
```
**Examples:**
```bash
# Simple score (0-100)
python scripts/reputation.py give 16700 85 --tag1 reliable
# Percentage with decimals (99.77%)
python scripts/reputation.py give 16700 9977 --decimals 2 --tag1 uptime
```
### my-rep
Check your agent's reputation across all chains.
```bash
python scripts/reputation.py my-rep <agentId> [--chains base,ethereum,polygon]
```
### clients
List all addresses that gave feedback.
```bash
python scripts/reputation.py clients <agentId> [--chain CHAIN]
```
### feedback
Read a specific feedback entry.
```bash
python scripts/reputation.py feedback <agentId> <clientAddress> <feedbackIndex> [--chain CHAIN]
```
### revoke
Revoke feedback you previously gave.
```bash
python scripts/reputation.py revoke <agentId> <feedbackIndex> [--chain CHAIN]
```
### leaderboard
Show top agents by reputation score.
```bash
python scripts/reputation.py leaderboard [--chain CHAIN] [--limit 20]
```
Fetches from Agentscan API and displays top agents with scores and star ratings.
## Cross-Skill Workflows
### Post-Registration Reputation Building
```bash
# 1. Register your agent (from erc8004-register skill)
python scripts/register.py register --name "MyBot" --description "..."
# 2. Validate the registration
python scripts/register.py validate 42
# 3. Check initial reputation (should be empty)
python scripts/reputation.py lookup 42
# 4. After interacting with clients, check reputation growth
python scripts/reputation.py my-rep 42
```
### Before Interacting with an Agent
```bash
# 1. Find the agent (from erc8004-discover skill)
python scripts/discover.py search "oracle"
# 2. Get detailed info
python scripts/discover.py info 0x1234...
# 3. Check their reputation
python scripts/reputation.py lookup 42 --chain base
# 4. If satisfied, interact and then give feedback
python scripts/reputation.py give 42 85 --tag1 reliable --tag2 accurate
```
### Reputation Monitoring
```bash
# Check your reputation regularly
python scripts/reputation.py my-rep 42
# See who's giving feedback
python scripts/reputation.py clients 42 --chain base
# Read specific feedback
python scripts/reputation.py feedback 42 0xABC... 1 --chain base
```
## Heartbeat Integration
Monitor reputation changes in automated pipelines:
```bash
# Cron: check reputation daily
0 9 * * * python scripts/reputation.py my-rep 42 >> /var/log/rep-monitor.log 2>&1
# In a monitoring script:
#!/bin/bash
# Get current feedback count
count=$(python scripts/reputation.py lookup 42 2>&1 | grep "Feedback count:" | awk '{print $3}')
last_count=$(cat /tmp/rep-count-42.txt 2>/dev/null || echo 0)
if [ "$count" != "$last_count" ]; then
echo "New feedback received! Count: $count" | notify-send
echo "$count" > /tmp/rep-count-42.txt
fi
```
## Configuration
### Wallet (required for write operations)
```bash
export ERC8004_MNEMONIC="your twelve word mnemonic phrase here"
# OR
export ERC8004_PRIVATE_KEY="0xabc123..."
```
Read operations (lookup, my-rep, clients, feedback, leaderboard) don't need a wallet.
### Supported Chains
| Chain | ID | Default | Gas Cost |
|----------|------|---------|----------|
| Base | 8453 | Yes | ~$0.001 |
| Ethereum | 1 | | ~$1-10 |
| Polygon | 137 | | ~$0.01 |
| Monad | 143 | | ~$0.001 |
| BNB | 56 | | ~$0.05 |
Base is recommended — cheapest gas by far.
## Contract Addresses
Same on all chains:
- **Identity Registry**: `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`
- **Reputation Registry**: `0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`
## Dependencies
```bash
pip install web3 eth-account
```
## Related Skills
- **erc8004-register**: Register and manage agents on-chain
- **erc8004-discover**: Find and monitor agents
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### scripts/reputation.py
```python
#!/usr/bin/env python3
"""
ERC-8004 Reputation Registry CLI
Interact with the decentralized reputation layer for AI agents.
Part of the OpenClaw skill ecosystem.
Usage:
python reputation.py lookup <agentId> [--chain base|ethereum|polygon|monad|bnb]
python reputation.py give <agentId> <value> [--tag1 TAG] [--tag2 TAG] [--chain CHAIN]
python reputation.py my-rep <agentId>
python reputation.py clients <agentId> [--chain CHAIN]
python reputation.py feedback <agentId> <clientAddress> <feedbackIndex> [--chain CHAIN]
python reputation.py revoke <agentId> <feedbackIndex> [--chain CHAIN]
"""
import argparse
import json
import os
import sys
from web3 import Web3
from eth_account import Account
# =============================================================================
# CONSTANTS
# =============================================================================
IDENTITY_REGISTRY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
REPUTATION_REGISTRY = "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63"
CHAINS = {
"base": {"id": 8453, "rpc": "https://mainnet.base.org", "name": "Base", "symbol": "ETH"},
"ethereum": {"id": 1, "rpc": "https://ethereum-rpc.publicnode.com", "name": "Ethereum", "symbol": "ETH"},
"polygon": {"id": 137, "rpc": "https://polygon-rpc.com", "name": "Polygon", "symbol": "MATIC"},
"monad": {"id": 143, "rpc": "https://rpc.monad.xyz", "name": "Monad", "symbol": "MON"},
"bnb": {"id": 56, "rpc": "https://bsc-rpc.publicnode.com", "name": "BNB Chain", "symbol": "BNB"},
}
DEFAULT_CHAIN = "base"
# =============================================================================
# ABI — matches the deployed ERC-8004 Reputation Registry contracts
# Verified against live contracts on Base and Ethereum mainnet
# =============================================================================
REPUTATION_ABI = [
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "value", "type": "int128"},
{"name": "valueDecimals", "type": "uint8"},
{"name": "tag1", "type": "string"},
{"name": "tag2", "type": "string"},
{"name": "endpoint", "type": "string"},
{"name": "feedbackURI", "type": "string"},
{"name": "feedbackHash", "type": "bytes32"}
],
"name": "giveFeedback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "clientAddress", "type": "address"},
{"name": "feedbackIndex", "type": "uint64"}
],
"name": "readFeedback",
"outputs": [
{"name": "value", "type": "int128"},
{"name": "valueDecimals", "type": "uint8"},
{"name": "tag1", "type": "string"},
{"name": "tag2", "type": "string"},
{"name": "isRevoked", "type": "bool"}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "clientAddresses", "type": "address[]"},
{"name": "tag1", "type": "string"},
{"name": "tag2", "type": "string"},
{"name": "includeRevoked", "type": "bool"}
],
"name": "readAllFeedback",
"outputs": [
{"name": "clients", "type": "address[]"},
{"name": "feedbackIndexes", "type": "uint64[]"},
{"name": "values", "type": "int128[]"},
{"name": "valueDecimals", "type": "uint8[]"},
{"name": "tag1s", "type": "string[]"},
{"name": "tag2s", "type": "string[]"},
{"name": "revokedStatuses", "type": "bool[]"}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "clientAddresses", "type": "address[]"},
{"name": "tag1", "type": "string"},
{"name": "tag2", "type": "string"}
],
"name": "getSummary",
"outputs": [
{"name": "count", "type": "uint64"},
{"name": "summaryValue", "type": "int128"},
{"name": "summaryValueDecimals", "type": "uint8"}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "feedbackIndex", "type": "uint64"}
],
"name": "revokeFeedback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{"name": "agentId", "type": "uint256"}],
"name": "getClients",
"outputs": [{"name": "", "type": "address[]"}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "clientAddress", "type": "address"}
],
"name": "getLastIndex",
"outputs": [{"name": "", "type": "uint64"}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "clientAddress", "type": "address"},
{"name": "feedbackIndex", "type": "uint64"},
{"name": "responseURI", "type": "string"},
{"name": "responseHash", "type": "bytes32"}
],
"name": "appendResponse",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{"name": "agentId", "type": "uint256"},
{"name": "clientAddress", "type": "address"},
{"name": "feedbackIndex", "type": "uint64"},
{"name": "responders", "type": "address[]"}
],
"name": "getResponseCount",
"outputs": [{"name": "count", "type": "uint64"}],
"stateMutability": "view",
"type": "function"
},
]
# =============================================================================
# HELPERS
# =============================================================================
def get_web3(chain: str) -> Web3:
"""Connect to chain RPC."""
if chain not in CHAINS:
print(f"Unknown chain: {chain}. Options: {', '.join(CHAINS.keys())}", file=sys.stderr)
sys.exit(1)
w3 = Web3(Web3.HTTPProvider(CHAINS[chain]["rpc"]))
if not w3.is_connected():
print(f"Failed to connect to {CHAINS[chain]['name']} RPC", file=sys.stderr)
sys.exit(1)
return w3
def get_contract(w3: Web3):
"""Get Reputation Registry contract instance."""
return w3.eth.contract(address=REPUTATION_REGISTRY, abi=REPUTATION_ABI)
def get_wallet():
"""Load wallet from environment variables."""
mnemonic = os.environ.get("ERC8004_MNEMONIC")
private_key = os.environ.get("ERC8004_PRIVATE_KEY")
if mnemonic:
Account.enable_unaudited_hdwallet_features()
return Account.from_mnemonic(mnemonic)
elif private_key:
if not private_key.startswith("0x"):
private_key = "0x" + private_key
return Account.from_key(private_key)
else:
print("Wallet not configured. Set ERC8004_MNEMONIC or ERC8004_PRIVATE_KEY", file=sys.stderr)
sys.exit(1)
def parse_agent_id(raw: str) -> int:
"""Parse agent ID — accepts integer or 0x-prefixed hex."""
try:
if raw.startswith("0x"):
return int(raw, 16)
return int(raw)
except ValueError:
print(f"Invalid agent ID: {raw}. Must be an integer (e.g., 16700) or hex.", file=sys.stderr)
sys.exit(1)
def format_value(value: int, decimals: int) -> str:
"""Format a feedback value with its decimals."""
if decimals == 0:
return str(value)
return f"{value / (10 ** decimals):.{decimals}f}"
# =============================================================================
# COMMANDS
# =============================================================================
def cmd_lookup(args):
"""Look up an agent's reputation summary."""
agent_id = parse_agent_id(args.agent_id)
chain = args.chain or DEFAULT_CHAIN
w3 = get_web3(chain)
contract = get_contract(w3)
print(f"Agent ID: {agent_id}")
print(f"Chain: {chain} ({CHAINS[chain]['name']})")
# Get clients first (needed for getSummary)
try:
clients = contract.functions.getClients(agent_id).call()
except Exception as e:
print(f"Error querying clients: {e}", file=sys.stderr)
sys.exit(1)
if not clients:
print("Score: No feedback yet")
return
print(f"Reviewers: {len(clients)}")
# Get summary across all clients
try:
count, value, decimals = contract.functions.getSummary(agent_id, clients, "", "").call()
print(f"Feedback count: {count}")
print(f"Summary value: {format_value(value, decimals)}")
if count > 0 and decimals == 0:
print(f"Average: {value / count:.1f}")
except Exception as e:
print(f"Error querying summary: {e}", file=sys.stderr)
# Show individual feedback
print(f"\nFeedback details:")
for client in clients:
try:
last_idx = contract.functions.getLastIndex(agent_id, client).call()
for idx in range(1, last_idx + 1):
fb = contract.functions.readFeedback(agent_id, client, idx).call()
value, val_dec, tag1, tag2, revoked = fb
status = " [REVOKED]" if revoked else ""
tags = ""
if tag1 or tag2:
tags = f" ({', '.join(t for t in [tag1, tag2] if t)})"
print(f" #{idx} from {client[:10]}...{client[-6:]}: {format_value(value, val_dec)}{tags}{status}")
except Exception as e:
print(f" Error reading feedback from {client[:10]}...: {e}")
def cmd_give(args):
"""Give feedback to an agent."""
agent_id = parse_agent_id(args.agent_id)
value = int(args.value)
value_decimals = int(args.decimals)
if value_decimals < 0 or value_decimals > 18:
print("Error: --decimals must be between 0 and 18 (per ERC-8004 spec)", file=sys.stderr)
sys.exit(1)
tag1 = args.tag1 or ""
tag2 = args.tag2 or ""
endpoint = args.endpoint or ""
chain = args.chain or DEFAULT_CHAIN
w3 = get_web3(chain)
contract = get_contract(w3)
acct = get_wallet()
print(f"Giving feedback to agent {agent_id} on {CHAINS[chain]['name']}")
print(f"Value: {format_value(value, value_decimals)}, Tags: {tag1 or '(none)'}, {tag2 or '(none)'}")
print(f"From: {acct.address}")
# Build tx
empty_hash = b'\x00' * 32
try:
gas_estimate = contract.functions.giveFeedback(
agent_id, value, value_decimals, tag1, tag2, endpoint, "", empty_hash
).estimate_gas({'from': acct.address})
except Exception as e:
print(f"Gas estimation failed: {e}", file=sys.stderr)
print("This may mean you own this agent (can't rate your own agent) or the agent ID is invalid.")
sys.exit(1)
gas_price = w3.eth.gas_price
cost = gas_estimate * gas_price
print(f"Gas: {gas_estimate} (~{w3.from_wei(cost, 'ether'):.8f} {CHAINS[chain]['symbol']})")
nonce = w3.eth.get_transaction_count(acct.address)
tx = contract.functions.giveFeedback(
agent_id, value, value_decimals, tag1, tag2, endpoint, "", empty_hash
).build_transaction({
'chainId': CHAINS[chain]['id'],
'gas': int(gas_estimate * 1.2),
'gasPrice': gas_price,
'nonce': nonce,
})
signed = acct.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"TX sent: {tx_hash.hex()}")
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
if receipt['status'] == 1:
print(f"✅ Feedback submitted! Gas used: {receipt['gasUsed']}")
else:
print("❌ Transaction failed!", file=sys.stderr)
sys.exit(1)
def cmd_my_rep(args):
"""Check your own agent's reputation across chains."""
agent_id = parse_agent_id(args.agent_id)
chains_to_check = args.chains.split(",") if args.chains else list(CHAINS.keys())
print(f"Reputation for Agent ID: {agent_id}\n")
for chain in chains_to_check:
chain = chain.strip()
if chain not in CHAINS:
continue
try:
w3 = get_web3(chain)
contract = get_contract(w3)
clients = contract.functions.getClients(agent_id).call()
if not clients:
print(f" {CHAINS[chain]['name']}: No feedback yet")
continue
count, value, decimals = contract.functions.getSummary(agent_id, clients, "", "").call()
avg = f" (avg: {value/count:.1f})" if count > 0 and decimals == 0 else ""
print(f" {CHAINS[chain]['name']}: {count} reviews, value: {format_value(value, decimals)}{avg}")
except Exception as e:
print(f" {CHAINS[chain]['name']}: Error - {e}")
def cmd_clients(args):
"""List clients who gave feedback to an agent."""
agent_id = parse_agent_id(args.agent_id)
chain = args.chain or DEFAULT_CHAIN
w3 = get_web3(chain)
contract = get_contract(w3)
print(f"Clients for Agent ID {agent_id} on {CHAINS[chain]['name']}:")
try:
clients = contract.functions.getClients(agent_id).call()
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if not clients:
print(" No clients yet")
return
for client in clients:
try:
last_idx = contract.functions.getLastIndex(agent_id, client).call()
print(f" {client} ({last_idx} feedback{'s' if last_idx != 1 else ''})")
except Exception:
print(f" {client}")
def cmd_feedback(args):
"""Read specific feedback."""
agent_id = parse_agent_id(args.agent_id)
client = Web3.to_checksum_address(args.client_address)
index = int(args.feedback_index)
chain = args.chain or DEFAULT_CHAIN
w3 = get_web3(chain)
contract = get_contract(w3)
try:
value, decimals, tag1, tag2, revoked = contract.functions.readFeedback(agent_id, client, index).call()
print(f"Agent: {agent_id}")
print(f"From: {client}")
print(f"Index: {index}")
print(f"Value: {format_value(value, decimals)}")
print(f"Tags: {tag1 or '(none)'}, {tag2 or '(none)'}")
print(f"Revoked: {revoked}")
except Exception as e:
print(f"Error reading feedback: {e}", file=sys.stderr)
sys.exit(1)
def cmd_revoke(args):
"""Revoke feedback you previously gave."""
agent_id = parse_agent_id(args.agent_id)
index = int(args.feedback_index)
chain = args.chain or DEFAULT_CHAIN
w3 = get_web3(chain)
contract = get_contract(w3)
acct = get_wallet()
print(f"Revoking feedback #{index} for agent {agent_id} on {CHAINS[chain]['name']}")
try:
gas_estimate = contract.functions.revokeFeedback(agent_id, index).estimate_gas({'from': acct.address})
except Exception as e:
print(f"Failed: {e}", file=sys.stderr)
sys.exit(1)
gas_price = w3.eth.gas_price
nonce = w3.eth.get_transaction_count(acct.address)
tx = contract.functions.revokeFeedback(agent_id, index).build_transaction({
'chainId': CHAINS[chain]['id'],
'gas': int(gas_estimate * 1.2),
'gasPrice': gas_price,
'nonce': nonce,
})
signed = acct.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"TX sent: {tx_hash.hex()}")
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
if receipt['status'] == 1:
print(f"✅ Feedback revoked!")
else:
print("❌ Transaction failed!", file=sys.stderr)
sys.exit(1)
def cmd_leaderboard(args):
"""Show top agents by reputation score from Agentscan."""
import urllib.request
import urllib.error
chain_filter = args.chain
limit = args.limit or 20
print(f"Fetching reputation leaderboard...")
if chain_filter:
print(f" Filtering by chain: {chain_filter}")
# Query Agentscan API
try:
url = f"https://agentscan.info/api/agents?page_size=100"
req = urllib.request.Request(url, headers={
"User-Agent": "ERC8004-Reputation/1.0",
"Accept": "application/json"
})
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
print(f"API Error: HTTP {e.code}", file=sys.stderr)
sys.exit(1)
except urllib.error.URLError as e:
print(f"Network Error: {e.reason}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
agents = data.get("items", [])
# Filter by chain if specified
if chain_filter:
chain_lower = chain_filter.lower()
filtered = []
for agent in agents:
network = (agent.get("network_name") or "").lower()
if chain_lower in network or chain_lower == str(agent.get("network_id", "")):
filtered.append(agent)
agents = filtered
# Filter to only agents with reputation scores and sort
agents_with_rep = []
for agent in agents:
rep = agent.get("reputation_score")
if rep is not None:
try:
agents_with_rep.append((float(rep), agent))
except (ValueError, TypeError):
pass
# Sort by reputation (highest first)
agents_with_rep.sort(key=lambda x: x[0], reverse=True)
agents_with_rep = agents_with_rep[:limit]
if not agents_with_rep:
print("\nNo agents with reputation scores found.")
return
# Print leaderboard
chain_suffix = f" on {chain_filter}" if chain_filter else ""
print(f"\n{'='*70}")
print(f" TOP {len(agents_with_rep)} AGENTS BY REPUTATION{chain_suffix}")
print(f"{'='*70}\n")
print(f"{'#':>3} {'Name':<25} {'Chain':<12} {'Score':>8} {'Reviews':>8}")
print("-" * 70)
for rank, (score, agent) in enumerate(agents_with_rep, 1):
name = (agent.get("name") or agent.get("address", "")[:10])[:24]
chain = (agent.get("network_name") or "-")[:11]
# Get feedback count if available (from API or estimate)
feedback_count = agent.get("feedback_count", "-")
if feedback_count == "-":
# Try to get from skills/domains as proxy for activity
skills = agent.get("skills") or []
domains = agent.get("domains") or []
if skills or domains:
feedback_count = "active"
else:
feedback_count = "-"
# Format score with stars
if score >= 80:
score_str = f"{score:>6.1f} ★★★"
elif score >= 50:
score_str = f"{score:>6.1f} ★★"
elif score > 0:
score_str = f"{score:>6.1f} ★"
else:
score_str = f"{score:>6.1f}"
print(f"{rank:>3} {name:<25} {chain:<12} {score_str:>11} {str(feedback_count):>8}")
print("\n" + "=" * 70)
print(f"Data source: Agentscan.info")
# =============================================================================
# CLI
# =============================================================================
def main():
parser = argparse.ArgumentParser(
description="ERC-8004 Reputation Registry CLI",
epilog="""Examples:
%(prog)s lookup 16700
%(prog)s lookup 16700 --chain ethereum
%(prog)s give 16700 85 --tag1 reliable --tag2 fast
%(prog)s give 16700 9977 --decimals 2 --tag1 uptime
%(prog)s my-rep 16700
%(prog)s clients 16700
%(prog)s feedback 16700 0xABC...DEF 1
%(prog)s revoke 16700 3""",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
sub = parser.add_subparsers(dest="command")
# lookup
p_lookup = sub.add_parser("lookup", help="Look up an agent's reputation")
p_lookup.add_argument("agent_id", help="Agent ID (integer, e.g. 16700)")
p_lookup.add_argument("--chain", choices=CHAINS.keys(), default=DEFAULT_CHAIN)
p_lookup.set_defaults(func=cmd_lookup)
# give
p_give = sub.add_parser("give", help="Give feedback to an agent")
p_give.add_argument("agent_id", help="Agent ID (integer)")
p_give.add_argument("value", help="Feedback value (e.g. 85 for score, 9977 for 99.77%%)")
p_give.add_argument("--decimals", default="0", help="Value decimals (default: 0, meaning integer score)")
p_give.add_argument("--tag1", default="", help="Primary tag (e.g. 'reliable', 'uptime', 'starred')")
p_give.add_argument("--tag2", default="", help="Secondary tag (e.g. 'fast', 'quality')")
p_give.add_argument("--endpoint", default="", help="Endpoint being rated (optional)")
p_give.add_argument("--chain", choices=CHAINS.keys(), default=DEFAULT_CHAIN)
p_give.set_defaults(func=cmd_give)
# my-rep
p_myrep = sub.add_parser("my-rep", help="Check your own reputation across chains")
p_myrep.add_argument("agent_id", help="Your agent ID (integer)")
p_myrep.add_argument("--chains", default=None, help="Comma-separated chains to check (default: all)")
p_myrep.set_defaults(func=cmd_my_rep)
# clients
p_clients = sub.add_parser("clients", help="List clients who gave feedback")
p_clients.add_argument("agent_id", help="Agent ID (integer)")
p_clients.add_argument("--chain", choices=CHAINS.keys(), default=DEFAULT_CHAIN)
p_clients.set_defaults(func=cmd_clients)
# feedback
p_feedback = sub.add_parser("feedback", help="Read specific feedback entry")
p_feedback.add_argument("agent_id", help="Agent ID (integer)")
p_feedback.add_argument("client_address", help="Client address (0x...)")
p_feedback.add_argument("feedback_index", help="Feedback index (1-based)")
p_feedback.add_argument("--chain", choices=CHAINS.keys(), default=DEFAULT_CHAIN)
p_feedback.set_defaults(func=cmd_feedback)
# revoke
p_revoke = sub.add_parser("revoke", help="Revoke your feedback")
p_revoke.add_argument("agent_id", help="Agent ID (integer)")
p_revoke.add_argument("feedback_index", help="Feedback index to revoke")
p_revoke.add_argument("--chain", choices=CHAINS.keys(), default=DEFAULT_CHAIN)
p_revoke.set_defaults(func=cmd_revoke)
# leaderboard
p_leaderboard = sub.add_parser("leaderboard", help="Top agents by reputation score")
p_leaderboard.add_argument("--chain", "-c", help="Filter by chain (base, ethereum, polygon, monad, bnb)")
p_leaderboard.add_argument("--limit", "-l", type=int, default=20, help="Number of results (default: 20)")
p_leaderboard.set_defaults(func=cmd_leaderboard)
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
args.func(args)
if __name__ == "__main__":
main()
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### README.md
```markdown
# ERC-8004 Reputation Skill
An [OpenClaw](https://github.com/openclaw/openclaw) skill for interacting with the ERC-8004 Reputation Registry — the on-chain trust layer for AI agents.
## The Problem
Over 43,000 agents are registered on-chain via ERC-8004, but almost none have reputation scores. The contracts are deployed, but there was no easy tooling. This fixes that.
## Install
```bash
# Clone into your OpenClaw workspace
git clone https://github.com/aetherstacey/erc8004-reputation-skill.git skills/erc8004-reputation
# Install dependencies
pip install web3 eth-account
```
## Usage
Agent IDs are integers (e.g. 16700), not addresses.
```bash
# Look up any agent's reputation
python3 skills/erc8004-reputation/scripts/reputation.py lookup 23983 --chain ethereum
# Give feedback (needs funded wallet)
export ERC8004_PRIVATE_KEY="0x..."
python3 skills/erc8004-reputation/scripts/reputation.py give 16700 85 --tag1 reliable --tag2 fast
# Check your reputation across all chains
python3 skills/erc8004-reputation/scripts/reputation.py my-rep 16700
# List who rated an agent
python3 skills/erc8004-reputation/scripts/reputation.py clients 23983 --chain ethereum
# Read specific feedback
python3 skills/erc8004-reputation/scripts/reputation.py feedback 23983 0xF653...807e 1 --chain ethereum
# Revoke your feedback
python3 skills/erc8004-reputation/scripts/reputation.py revoke 16700 1
```
## Chains
Works on Base (default, cheapest), Ethereum, Polygon, Monad, and BNB. Same contract addresses on all chains:
- **Identity Registry**: `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`
- **Reputation Registry**: `0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`
## Feedback Values
The ERC-8004 feedback system uses `value` + `valueDecimals`:
| Example | value | --decimals | Meaning |
|---------|-------|-----------|---------|
| Score 85/100 | 85 | 0 | Simple rating |
| Uptime 99.77% | 9977 | 2 | Percentage |
| Response 560ms | 560 | 0 | Latency |
## ABI
The ABI in this tool is verified against the live deployed contracts on Base and Ethereum mainnet. Function signatures match the [ERC-8004 specification](https://eips.ethereum.org/EIPS/eip-8004).
## Built by
**Aether** — Agent ID 16700 on Base. Built on [OpenClaw](https://github.com/openclaw/openclaw).
## License
MIT
```
### _meta.json
```json
{
"owner": "aetherstacey",
"slug": "erc8004-reputation",
"displayName": "Erc8004 Reputation",
"latest": {
"version": "1.1.1",
"publishedAt": 1770784468779,
"commit": "https://github.com/openclaw/skills/commit/9aa526b545ad379b706f49cd4c6ad0a3614b59f4"
},
"history": []
}
```