Skill: sumeetchougule/chaoschain (version 1.0.2)

--- name: chaoschain description: Verify AI agent identity and reputation via ERC-8004 on-chain registries user-invocable: true metadata: {"openclaw": {"emoji": "⛓️", "requires": {"bins": ["python3"]}, "homepage": "https://chaoscha.in", "skillKey": "chaoschain"}} --- # ChaosChain - On-Chain Agent Trust & Reputation ChaosChain is the **trust layer for AI agents**. This skill lets you verify agent identities and check on-chain reputation scores from the [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) registries. ## What This Skill Does ✅ **Verify** - Check if an agent has on-chain identity ✅ **Reputation** - View multi-dimensional reputation scores ✅ **Trust** - Make informed decisions before trusting other agents ❌ This skill does NOT execute workflows, submit work, or handle payments. ❌ This is a READ-ONLY trust visualization tool by default. ## Commands ### `/chaoschain verify <agent_id_or_address>` Check if an agent is registered on ERC-8004 and view their basic info. ``` /chaoschain verify 450 /chaoschain verify 0x1234...abcd ``` Returns: - Registration status - Agent name and domain (if available) - Owner address - Trust score summary ### `/chaoschain reputation <agent_id_or_address>` View detailed multi-dimensional reputation scores for an agent. ``` /chaoschain reputation 450 ``` Returns 5 Proof of Agency dimensions: - Initiative - Collaboration - Reasoning - Compliance - Efficiency ### `/chaoschain whoami` Check if YOUR agent wallet has an on-chain identity. ``` /chaoschain whoami ``` Requires `CHAOSCHAIN_PRIVATE_KEY` or `CHAOSCHAIN_ADDRESS` to be set. ### `/chaoschain register` (OPTIONAL - On-Chain Action) ⚠️ **WARNING: This command submits an on-chain transaction.** Register your agent on the ERC-8004 IdentityRegistry. ``` /chaoschain register # Defaults to Sepolia (safe) /chaoschain register --network sepolia # Recommended for testing /chaoschain register --network mainnet # Advanced users only ``` Requirements: - `CHAOSCHAIN_PRIVATE_KEY` must be set - Wallet must have ETH for gas (~0.001 ETH) - This is a ONE-TIME action per wallet **Safety Default**: Registration defaults to **Sepolia testnet** to prevent accidental mainnet transactions. Use `--network mainnet` explicitly for production. ## Network Defaults | Command | Default Network | Reason | |---------|-----------------|--------| | `verify` | Mainnet | Production reputation data | | `reputation` | Mainnet | Production reputation data | | `whoami` | Mainnet | Check production identity | | `register` | **Sepolia** | Safety - avoid accidental mainnet txs | Override with `--network <network_key>`: ``` /chaoschain verify 450 --network base_mainnet /chaoschain register --network ethereum_mainnet ``` ## Setup ### After Installation (Required Once) Run the setup script to install Python dependencies: ```bash cd ~/.openclaw/skills/chaoschain ./scripts/setup.sh ``` This creates a virtual environment with `web3` and other dependencies. ### Read-Only Mode (Default) No setup required after running `setup.sh`! Just use `/chaoschain verify` and `/chaoschain reputation`. ### With Your Own Wallet (Optional) To use `/chaoschain whoami` or `/chaoschain register`, add to your OpenClaw config: ```json { "skills": { "entries": { "chaoschain": { "enabled": true, "env": { "CHAOSCHAIN_ADDRESS": "0xYourAddress...", "CHAOSCHAIN_NETWORK": "mainnet" } } } } } ``` For registration (on-chain action): ```json { "skills": { "entries": { "chaoschain": { "enabled": true, "env": { "CHAOSCHAIN_PRIVATE_KEY": "0x...", "CHAOSCHAIN_NETWORK": "mainnet" } } } } } ``` ### Network Options Mainnet keys (same official ERC-8004 registries): - `ethereum_mainnet` - `base_mainnet` - `polygon_mainnet` - `arbitrum_mainnet` - `celo_mainnet` - `gnosis_mainnet` - `scroll_mainnet` - `taiko_mainnet` - `monad_mainnet` - `bsc_mainnet` Testnet keys (same official ERC-8004 registries): - `ethereum_sepolia` - `base_sepolia` - `polygon_amoy` - `arbitrum_testnet` - `celo_testnet` - `scroll_testnet` - `monad_testnet` - `bsc_testnet` - `optimism_sepolia` - `linea_sepolia` - `mode_testnet` Backward-compatible aliases: - `mainnet` -> `ethereum_mainnet` - `sepolia` -> `ethereum_sepolia` ## Example Usage **Before trusting an agent to help with a task:** ``` User: /chaoschain verify 550 Agent: ⛓️ Agent #550 Verification ━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ REGISTERED on ERC-8004 Name: DataAnalyzer Domain: analyzer.ai Owner: 0x2A47...8B8 Trust Summary: 87/100 (HIGH) Total Feedback: 23 reviews This agent has verified on-chain identity. ``` **Check detailed reputation:** ``` User: /chaoschain reputation 550 Agent: ⛓️ Agent #550 Reputation ━━━━━━━━━━━━━━━━━━━━━━━━━━ Initiative: ████████░░ 81/100 Collaboration: █████████░ 89/100 Reasoning: █████████░ 88/100 Compliance: ████████░░ 84/100 Efficiency: █████████░ 93/100 Overall: 87/100 (HIGH TRUST) Based on 23 on-chain feedback entries. ``` ## What is ERC-8004? [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) is the Ethereum standard for **Trustless Agents**. It provides: - **IdentityRegistry** - On-chain agent registration (NFT-based) - **ReputationRegistry** - Feedback and reputation scores - **ValidationRegistry** - Independent validation records ChaosChain is a reference implementation of ERC-8004. ## Contract Addresses | Network | Registry | Address | |---------|----------|---------| | Mainnet (all supported mainnet chains) | Identity | [REDACTED_SECRET] | | Mainnet (all supported mainnet chains) | Reputation | [REDACTED_SECRET] | | Testnet (all supported testnet chains) | Identity | [REDACTED_SECRET] | | Testnet (all supported testnet chains) | Reputation | [REDACTED_SECRET] | ## Learn More - [ChaosChain Documentation](https://docs.chaoscha.in) - [ERC-8004 Specification](https://eips.ethereum.org/EIPS/eip-8004) - [8004scan.io](https://8004scan.io) - Agent Explorer - [GitHub](https://github.com/ChaosChain/chaoschain) ## Security Notes - This skill is **READ-ONLY by default** - `/chaoschain register` is the ONLY command that writes on-chain - Private keys are only used for registration, never for viewing - No funds are transferred, only gas for registration - Source code: `{baseDir}/scripts/`

path: requirements.txt
content: # ChaosChain OpenClaw Skill Dependencies
# Minimal dependencies for trust verification

web3>=6.0.0
eth-account>=0.8.0


path: README.md
content: # ChaosChain OpenClaw Skill

**Trust & Reputation Verification for AI Agents**

This OpenClaw skill provides READ-ONLY access to ERC-8004 on-chain registries, allowing you to verify agent identities and check reputation scores before trusting other agents.

## What This Is

✅ **Trust Visualization Tool** - See on-chain reputation scores  
✅ **Agent Verification** - Check if an agent is registered on ERC-8004  
✅ **READ-ONLY by Default** - No transactions, no custody, no risk  

## What This Is NOT

❌ This is NOT a workflow execution tool  
❌ This does NOT submit work or scores  
❌ This does NOT handle payments  
❌ This does NOT run background processes  
❌ This does NOT interact with ChaosChain Gateway  

## Installation

### From ClawHub (Recommended)

```bash
clawhub install chaoschain
```

### Manual Installation

Copy the `chaoschain/` folder to your OpenClaw skills directory:

```bash
cp -r chaoschain ~/.openclaw/skills/
```

Or to your workspace:

```bash
cp -r chaoschain ~/your-workspace/skills/
```

## Commands

| Command | Description | Requires Wallet? | Default Network |
|---------|-------------|------------------|-----------------|
| `/chaoschain verify <id>` | Check if agent is registered | No | Mainnet |
| `/chaoschain reputation <id>` | View reputation scores | No | Mainnet |
| `/chaoschain whoami` | Check your identity | Address only | Mainnet |
| `/chaoschain register` | Register on ERC-8004 | Yes (on-chain tx) | **Sepolia** |

### Network Flag

All commands support `--network <network_key>`:

```bash
/chaoschain verify 450 --network base_mainnet
/chaoschain register --network ethereum_sepolia  # Recommended
```

**Safety Default**: Registration defaults to Sepolia to prevent accidental mainnet transactions.

## Usage Examples

### Verify an Agent

```
/chaoschain verify 450
```

Output:
```
⛓️ Verifying agent: 450
   Network: MAINNET
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ REGISTERED on ERC-8004

Agent ID: #450
Owner: 0x1234...abcd
Name: DataAnalyzer
Description: AI data analysis agent...

Trust Score: 87/100 (✅ HIGH TRUST)
Total Feedback: 23 reviews

🔗 https://8004scan.io/agents/mainnet/450
```

### Check Reputation

```
/chaoschain reputation 450
```

Output:
```
⛓️ Agent #450 Reputation
   Network: MAINNET
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Initiative     ████████░░  81/100
Collaboration  █████████░  89/100
Reasoning      █████████░  88/100
Compliance     ████████░░  84/100
Efficiency     █████████░  93/100

Overall: 87/100 (✅ HIGH TRUST)
Based on 23 on-chain feedback entries.

🔗 https://8004scan.io/agents/mainnet/450
```

## Configuration

### Read-Only Mode (Default)

No configuration needed. Just use the skill.

### With Your Wallet (Optional)

Add to `~/.openclaw/openclaw.json`:

```json
{
  "skills": {
    "entries": {
      "chaoschain": {
        "enabled": true,
        "env": {
          "CHAOSCHAIN_ADDRESS": "0xYourAddress...",
          "CHAOSCHAIN_NETWORK": "mainnet"
        }
      }
    }
  }
}
```

### For Registration (On-Chain Action)

⚠️ **Warning**: This enables on-chain transactions.

```json
{
  "skills": {
    "entries": {
      "chaoschain": {
        "enabled": true,
        "env": {
          "CHAOSCHAIN_PRIVATE_KEY": "0x...",
          "CHAOSCHAIN_NETWORK": "mainnet"
        }
      }
    }
  }
}
```

## Environment Variables

| Variable | Description | Required |
|----------|-------------|----------|
| `CHAOSCHAIN_NETWORK` | Network key (e.g. `ethereum_mainnet`, `base_mainnet`, `ethereum_sepolia`) | No (default: `ethereum_mainnet` for read, `ethereum_sepolia` for register) |
| `CHAOSCHAIN_ADDRESS` | Your wallet address | For `/whoami` |
| `CHAOSCHAIN_PRIVATE_KEY` | Your private key | For `/register` |
| `CHAOSCHAIN_RPC_URL` | Custom RPC endpoint | No |

## Network Support

### Mainnet (same registry addresses on all)

`IdentityRegistry` [REDACTED_SECRET]  
`ReputationRegistry` [REDACTED_SECRET]

- `ethereum_mainnet`
- `base_mainnet`
- `polygon_mainnet`
- `arbitrum_mainnet`
- `celo_mainnet`
- `gnosis_mainnet`
- `scroll_mainnet`
- `taiko_mainnet`
- `monad_mainnet`
- `bsc_mainnet`

### Testnet (same registry addresses on all)

`IdentityRegistry` [REDACTED_SECRET]  
`ReputationRegistry` [REDACTED_SECRET]

- `ethereum_sepolia`
- `base_sepolia`
- `polygon_amoy`
- `arbitrum_testnet`
- `celo_testnet`
- `scroll_testnet`
- `monad_testnet`
- `bsc_testnet`
- `optimism_sepolia`
- `linea_sepolia`
- `mode_testnet`

Backward-compatible aliases:
- `mainnet` -> `ethereum_mainnet`
- `sepolia` -> `ethereum_sepolia`

## Security

- **READ-ONLY by default** - No transactions without explicit action
- **No custody** - Private keys are only used for `/register`
- **No background processes** - Only runs when you invoke a command
- **Open source** - Full source code in `scripts/chaoschain_skill.py`

## Dependencies

- Python 3.8+
- web3.py (`pip install web3`)

## What is ERC-8004?

[ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) is the Ethereum standard for **Trustless Agents**:

- **IdentityRegistry** - On-chain agent registration (NFT-based)
- **ReputationRegistry** - Feedback and reputation scores
- **ValidationRegistry** - Independent validation records

## What is ChaosChain?

ChaosChain is the **accountability layer for AI agents**. It provides:

- Multi-agent collaboration with fair attribution
- Consensus-based reputation from independent verifiers
- On-chain proof of work quality
- ERC-8004 compliant identity and reputation

Learn more: [https://chaoscha.in](https://chaoscha.in)

## Links

- [ChaosChain Documentation](https://docs.chaoscha.in)
- [ERC-8004 Specification](https://eips.ethereum.org/EIPS/eip-8004)
- [8004scan.io](https://8004scan.io) - Agent Explorer
- [GitHub](https://github.com/ChaosChain/chaoschain)

## License

MIT License - See [LICENSE](../../../LICENSE)


path: scripts/setup.sh
content: #!/bin/bash
# Setup script for ChaosChain OpenClaw Skill
# Run this after installing the skill via ClawHub

set -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SKILL_DIR="$( dirname "$SCRIPT_DIR" )"

echo "🔧 Setting up ChaosChain OpenClaw Skill..."
echo "   Skill directory: $SKILL_DIR"

# Create virtual environment
if [ ! -d "$SKILL_DIR/.venv" ]; then
    echo "📦 Creating Python virtual environment..."
    python3 -m venv "$SKILL_DIR/.venv"
fi

# Activate and install dependencies
echo "📚 Installing dependencies..."
source "$SKILL_DIR/.venv/bin/activate"
pip install --quiet --upgrade pip
pip install --quiet -r "$SKILL_DIR/requirements.txt"

# Make wrapper scripts executable
echo "🔑 Making scripts executable..."
chmod +x "$SCRIPT_DIR"/*.py 2>/dev/null || true

echo ""
echo "✅ ChaosChain skill setup complete!"
echo ""
echo "📖 Available commands:"
echo "   /chaoschain verify <agent_id>    - Verify agent identity"
echo "   /chaoschain reputation <agent_id> - Get reputation breakdown"
echo "   /chaoschain whoami               - Check your own identity"
echo "   /chaoschain register             - Register as ERC-8004 agent"
echo ""
echo "🔗 Explorer: https://8004scan.io"


path: scripts/reputation.py
content: #!/usr/bin/env python3
"""
Wrapper for reputation command - uses venv with web3 installed

OpenClaw expects separate scripts for each command.
This wrapper calls the main chaoschain_skill.py with the 'reputation' command.
"""
import sys
import os
import subprocess

scripts_dir = os.path.dirname(os.path.abspath(__file__))
skill_dir = os.path.dirname(scripts_dir)
venv_python = os.path.join(skill_dir, ".venv", "bin", "python3")
main_script = os.path.join(scripts_dir, "chaoschain_skill.py")

if os.path.exists(venv_python):
    python_exe = venv_python
else:
    python_exe = sys.executable

args = [python_exe, main_script, "reputation"] + sys.argv[1:]
sys.exit(subprocess.call(args))


path: scripts/verify_agent.py
content: #!/usr/bin/env python3
"""
Wrapper for verify command - uses venv with web3 installed

OpenClaw expects separate scripts for each command.
This wrapper calls the main chaoschain_skill.py with the 'verify' command.
"""
import sys
import os
import subprocess

# Get the skill directory
scripts_dir = os.path.dirname(os.path.abspath(__file__))
skill_dir = os.path.dirname(scripts_dir)
venv_python = os.path.join(skill_dir, ".venv", "bin", "python3")
main_script = os.path.join(scripts_dir, "chaoschain_skill.py")

# Check if venv exists, if not use system python
if os.path.exists(venv_python):
    python_exe = venv_python
else:
    python_exe = sys.executable

# Run with the appropriate python
args = [python_exe, main_script, "verify"] + sys.argv[1:]
sys.exit(subprocess.call(args))


path: scripts/register.py
content: #!/usr/bin/env python3
"""
Wrapper for register command - uses venv with web3 installed

OpenClaw expects separate scripts for each command.
This wrapper calls the main chaoschain_skill.py with the 'register' command.

⚠️ WARNING: This command submits an on-chain transaction!
"""
import sys
import os
import subprocess

scripts_dir = os.path.dirname(os.path.abspath(__file__))
skill_dir = os.path.dirname(scripts_dir)
venv_python = os.path.join(skill_dir, ".venv", "bin", "python3")
main_script = os.path.join(scripts_dir, "chaoschain_skill.py")

if os.path.exists(venv_python):
    python_exe = venv_python
else:
    python_exe = sys.executable

args = [python_exe, main_script, "register"] + sys.argv[1:]
sys.exit(subprocess.call(args))


path: scripts/chaoschain_skill.py
content: #!/usr/bin/env python3
"""
ChaosChain OpenClaw Skill - Trust & Reputation Verification

This script provides READ-ONLY access to ERC-8004 registries for verifying
agent identity and reputation. No protocol execution, no payments, no Gateway.

Commands:
- verify <agent_id_or_address> - Check agent registration status
- reputation <agent_id_or_address> - View detailed reputation scores
- whoami - Check if your wallet has an on-chain identity
- register - (OPTIONAL) Register on ERC-8004 (on-chain action)

Environment Variables:
- CHAOSCHAIN_NETWORK: Network key (default: ethereum_mainnet for read, ethereum_sepolia for register)
- CHAOSCHAIN_ADDRESS: Your wallet address (for whoami, read-only)
- CHAOSCHAIN_PRIVATE_KEY: Your private key (ONLY for register command)
- CHAOSCHAIN_RPC_URL: Custom RPC URL (optional)
"""

import os
import sys
import json
from typing import Optional, Dict, Any, Tuple

# Official ERC-8004 registries (same per network group)
MAINNET_IDENTITY_REGISTRY = [REDACTED_SECRET]
MAINNET_REPUTATION_REGISTRY = [REDACTED_SECRET]
TESTNET_IDENTITY_REGISTRY = [REDACTED_SECRET]
TESTNET_REPUTATION_REGISTRY = [REDACTED_SECRET]

# Network support aligned with SDK NetworkConfig list.
CONTRACTS = {
    # Mainnet
    "ethereum_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://eth.llamarpc.com",
        "chain_id": 1,
        "explorer": "https://etherscan.io",
    },
    "base_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://base-rpc.publicnode.com",
        "chain_id": 8453,
        "explorer": "https://basescan.org",
    },
    "polygon_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://polygon-bor-rpc.publicnode.com",
        "chain_id": 137,
        "explorer": "https://polygonscan.com",
    },
    "arbitrum_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://arbitrum-one-rpc.publicnode.com",
        "chain_id": 42161,
        "explorer": "https://arbiscan.io",
    },
    "celo_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://celo-rpc.publicnode.com",
        "chain_id": 42220,
        "explorer": "https://celoscan.io",
    },
    "gnosis_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://gnosis-rpc.publicnode.com",
        "chain_id": 100,
        "explorer": "https://gnosisscan.io",
    },
    "scroll_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://scroll-rpc.publicnode.com",
        "chain_id": 534352,
        "explorer": "https://scrollscan.com",
    },
    "taiko_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://rpc.mainnet.taiko.xyz",
        "chain_id": 167000,
        "explorer": "https://taikoscan.io",
    },
    "monad_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://rpc.monad.xyz",
        "chain_id": 10143,
        "explorer": "https://explorer.monad.xyz",
    },
    "bsc_mainnet": {
        "identity_registry": MAINNET_IDENTITY_REGISTRY,
        "reputation_registry": MAINNET_REPUTATION_REGISTRY,
        "rpc_url": "https://bsc-rpc.publicnode.com",
        "chain_id": 56,
        "explorer": "https://bscscan.com",
    },
    # Testnet
    "ethereum_sepolia": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://ethereum-sepolia-rpc.publicnode.com",
        "chain_id": 11155111,
        "explorer": "https://sepolia.etherscan.io",
    },
    "base_sepolia": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://base-sepolia-rpc.publicnode.com",
        "chain_id": 84532,
        "explorer": "https://sepolia.basescan.org",
    },
    "polygon_amoy": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://polygon-amoy-bor-rpc.publicnode.com",
        "chain_id": 80002,
        "explorer": "https://amoy.polygonscan.com",
    },
    "arbitrum_testnet": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://arbitrum-sepolia-rpc.publicnode.com",
        "chain_id": 421614,
        "explorer": "https://sepolia.arbiscan.io",
    },
    "celo_testnet": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://celo-alfajores-rpc.publicnode.com",
        "chain_id": 44787,
        "explorer": "https://alfajores.celoscan.io",
    },
    "scroll_testnet": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://scroll-sepolia-rpc.publicnode.com",
        "chain_id": 534351,
        "explorer": "https://sepolia.scrollscan.com",
    },
    "monad_testnet": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://testnet-rpc.monad.xyz",
        "chain_id": 10143,
        "explorer": "https://testnet.monadexplorer.com",
    },
    "bsc_testnet": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://bsc-testnet-rpc.publicnode.com",
        "chain_id": 97,
        "explorer": "https://testnet.bscscan.com",
    },
    "optimism_sepolia": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://optimism-sepolia-rpc.publicnode.com",
        "chain_id": 11155420,
        "explorer": "https://sepolia-optimism.etherscan.io",
    },
    "linea_sepolia": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://linea-sepolia-rpc.publicnode.com",
        "chain_id": 59141,
        "explorer": "https://sepolia.lineascan.build",
    },
    "mode_testnet": {
        "identity_registry": TESTNET_IDENTITY_REGISTRY,
        "reputation_registry": TESTNET_REPUTATION_REGISTRY,
        "rpc_url": "https://sepolia.mode.network",
        "chain_id": 919,
        "explorer": "https://sepolia.explorer.mode.network",
    },
}

# Backward-compatible aliases
NETWORK_ALIASES = {
    "mainnet": "ethereum_mainnet",
    "sepolia": "ethereum_sepolia",
}

# Minimal ABIs for read operations
IDENTITY_ABI = [
    {"inputs": [{"name": "tokenId", "type": "uint256"}], "name": "ownerOf", "outputs": [{"name": "", "type": "address"}], "stateMutability": "view", "type": "function"},
    {"inputs": [{"name": "tokenId", "type": "uint256"}], "name": "tokenURI", "outputs": [{"name": "", "type": "string"}], "stateMutability": "view", "type": "function"},
    {"inputs": [{"name": "owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
    {"inputs": [{"name": "agentURI", "type": "string"}], "name": "register", "outputs": [{"name": "agentId", "type": "uint256"}], "stateMutability": "nonpayable", "type": "function"},
]

REPUTATION_ABI = [
    # Feb 2026 spec: returns (count, value, valueDecimals) instead of (count, averageScore)
    {"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": "value", "type": "int128"}, {"name": "valueDecimals", "type": "uint8"}], "stateMutability": "view", "type": "function"},
    {"inputs": [{"name": "agentId", "type": "uint256"}], "name": "getClients", "outputs": [{"name": "", "type": "address[]"}], "stateMutability": "view", "type": "function"},
]


def get_network_config(command: str = "read") -> Dict[str, Any]:
    """
    Get network configuration from environment.
    
    Network defaults:
    - READ operations (verify, reputation, whoami): Mainnet
    - WRITE operations (register): Sepolia (safety default)
    
    Users can override with CHAOSCHAIN_NETWORK or --network flag.
    """
    env_network = os.environ.get("CHAOSCHAIN_NETWORK", "").lower()
    
    if env_network:
        env_network = NETWORK_ALIASES.get(env_network, env_network)

    if env_network and env_network in CONTRACTS:
        network = env_network
    elif command == "register":
        # Safety default: registration goes to Sepolia to avoid accidents
        network = "ethereum_sepolia"
    else:
        # Read operations default to Mainnet (production data)
        network = "ethereum_mainnet"
    
    config = CONTRACTS[network].copy()
    
    # Allow custom RPC URL override
    custom_rpc = os.environ.get("CHAOSCHAIN_RPC_URL")
    if custom_rpc:
        config["rpc_url"] = custom_rpc
    
    config["network"] = network
    return config


def get_web3(command: str = "read", network_override: Optional[str] = None):
    """
    Initialize Web3 connection.
    
    Args:
        command: "read" or "register" - affects default network
        network_override: Explicit network from --network flag
    """
    try:
        from web3 import Web3
    except ImportError:
        print("❌ Error: web3 package not installed")
        print("   Run: pip install web3")
        sys.exit(1)
    
    # Handle explicit --network flag
    if network_override:
        os.environ["CHAOSCHAIN_NETWORK"] = network_override
    
    config = get_network_config(command)
    w3 = Web3(Web3.HTTPProvider(config["rpc_url"]))
    
    if not w3.is_connected():
        print(f"❌ Error: Cannot connect to {config['network']} RPC")
        sys.exit(1)
    
    return w3, config


def resolve_agent_id(w3, config: Dict, identifier: str) -> Tuple[Optional[int], Optional[str]]:
    """
    Resolve an identifier to an agent ID.
    Returns (agent_id, owner_address) or (None, None) if not found.
    """
    identity = w3.eth.contract(
        address=w3.to_checksum_address(config["identity_registry"]),
        abi=IDENTITY_ABI
    )
    
    # Check if it's a number (agent ID)
    if identifier.isdigit():
        agent_id = int(identifier)
        try:
            owner = identity.functions.ownerOf(agent_id).call()
            return agent_id, owner
        except Exception:
            return None, None
    
    # Check if it's an address
    if identifier.startswith("0x") and len(identifier) == 42:
        try:
            address = w3.to_checksum_address(identifier)
            balance = identity.functions.balanceOf(address).call()
            if balance > 0:
                # This address owns at least one agent, but we'd need to enumerate
                # For simplicity, return the address as owner
                return None, address
            return None, None
        except Exception:
            return None, None
    
    return None, None


def fetch_agent_uri(w3, config: Dict, agent_id: int) -> Optional[Dict]:
    """Fetch and parse agent URI metadata."""
    identity = w3.eth.contract(
        address=w3.to_checksum_address(config["identity_registry"]),
        abi=IDENTITY_ABI
    )
    
    try:
        uri = identity.functions.tokenURI(agent_id).call()
        
        # Handle data URI
        if uri.startswith("data:application/json;base64,"):
            import base64
            json_str = base64.b64decode(uri.split(",")[1]).decode("utf-8")
            return json.loads(json_str)
        
        # Handle IPFS or HTTP URI (simplified - just return the URI)
        return {"uri": uri}
    except Exception:
        return None


def fetch_reputation(w3, config: Dict, agent_id: int) -> Dict[str, Any]:
    """Fetch reputation summary for an agent."""
    reputation = w3.eth.contract(
        address=w3.to_checksum_address(config["reputation_registry"]),
        abi=REPUTATION_ABI
    )
    
    # First, get all clients who have given feedback
    try:
        clients = reputation.functions.getClients(agent_id).call()
    except Exception:
        clients = []
    
    if not clients:
        # No feedback yet
        return {
            "Initiative": {"count": 0, "score": 0},
            "Collaboration": {"count": 0, "score": 0},
            "Reasoning": {"count": 0, "score": 0},
            "Compliance": {"count": 0, "score": 0},
            "Efficiency": {"count": 0, "score": 0},
            "overall": {"count": 0, "score": 0}
        }
    
    # Proof of Agency dimensions
    dimensions = ["Initiative", "Collaboration", "Reasoning", "Compliance", "Efficiency"]
    results = {}
    
    for dim in dimensions:
        try:
            # Feb 2026 spec: returns (count, value, valueDecimals)
            # Must pass clientAddresses for Sybil resistance
            count, value, value_decimals = reputation.functions.getSummary(
                agent_id,
                clients,  # Required: list of client addresses
                dim,      # tag1
                ""        # tag2
            ).call()
            # Convert value based on decimals (for now decimals=0 means raw score)
            score = int(value) if value_decimals == 0 else int(value / (10 ** value_decimals))
            # Clamp to 0-100 range
            score = max(0, min(100, score))
            results[dim] = {"count": count, "score": score}
        except Exception as e:
            results[dim] = {"count": 0, "score": 0}
    
    # Overall summary
    try:
        total_count, value, value_decimals = reputation.functions.getSummary(
            agent_id,
            clients,
            "",
            ""
        ).call()
        score = int(value) if value_decimals == 0 else int(value / (10 ** value_decimals))
        score = max(0, min(100, score))
        results["overall"] = {"count": total_count, "score": score}
    except Exception:
        results["overall"] = {"count": 0, "score": 0}
    
    results["_clients"] = len(clients)  # For debugging
    
    return results


def render_progress_bar(score: int, width: int = 10) -> str:
    """Render a text progress bar."""
    filled = int((score / 100) * width)
    empty = width - filled
    return "█" * filled + "░" * empty


def trust_level(score: int) -> str:
    """Convert score to trust level."""
    if score >= 80:
        return "✅ HIGH TRUST"
    elif score >= 60:
        return "🟡 MODERATE"
    elif score >= 40:
        return "🟠 LOW"
    else:
        return "🔴 VERY LOW"


# ============================================================================
# COMMANDS
# ============================================================================

def cmd_verify(identifier: str, network: Optional[str] = None):
    """Verify an agent's on-chain identity."""
    w3, config = get_web3("read", network)
    
    print(f"⛓️ Verifying agent: {identifier}")
    print(f"   Network: {config['network'].upper()}")
    print("━" * 40)
    
    agent_id, owner = resolve_agent_id(w3, config, identifier)
    
    if agent_id is None and owner is None:
        print("❌ NOT REGISTERED on ERC-8004")
        print("")
        print("This identifier has no on-chain agent identity.")
        print("They can register at: https://chaoscha.in")
        return
    
    print(f"✅ REGISTERED on ERC-8004")
    print("")
    
    if agent_id:
        print(f"Agent ID: #{agent_id}")
        print(f"Owner: {owner}")
        
        # Fetch metadata
        metadata = fetch_agent_uri(w3, config, agent_id)
        if metadata:
            if "name" in metadata:
                print(f"Name: {metadata['name']}")
            if "description" in metadata:
                desc = metadata['description'][:100] + "..." if len(metadata.get('description', '')) > 100 else metadata.get('description', '')
                print(f"Description: {desc}")
        
        # Fetch reputation summary
        rep = fetch_reputation(w3, config, agent_id)
        overall = rep.get("overall", {})
        score = overall.get("score", 0)
        count = overall.get("count", 0)
        
        print("")
        print(f"Trust Score: {score}/100 ({trust_level(score)})")
        print(f"Total Feedback: {count} reviews")
        
        # Link to 8004scan
        print("")
        print(f"🔗 https://8004scan.io/agents/{config['network']}/{agent_id}")
    else:
        print(f"Address: {owner}")
        print("This address owns agent(s) but specific ID not resolved.")


def cmd_reputation(identifier: str, network: Optional[str] = None):
    """View detailed reputation scores for an agent."""
    w3, config = get_web3("read", network)
    
    agent_id, owner = resolve_agent_id(w3, config, identifier)
    
    if agent_id is None:
        print(f"❌ Cannot fetch reputation: Agent not found")
        print(f"   Identifier: {identifier}")
        return
    
    print(f"⛓️ Agent #{agent_id} Reputation")
    print(f"   Network: {config['network'].upper()}")
    print("━" * 40)
    
    rep = fetch_reputation(w3, config, agent_id)
    
    # Display each dimension
    dimensions = ["Initiative", "Collaboration", "Reasoning", "Compliance", "Efficiency"]
    
    for dim in dimensions:
        data = rep.get(dim, {"count": 0, "score": 0})
        score = data["score"]
        bar = render_progress_bar(score)
        print(f"{dim:14} {bar} {score:3}/100")
    
    print("")
    
    # Overall
    overall = rep.get("overall", {"count": 0, "score": 0})
    score = overall["score"]
    count = overall["count"]
    
    print(f"Overall: {score}/100 ({trust_level(score)})")
    print(f"Based on {count} on-chain feedback entries.")
    
    print("")
    print(f"🔗 https://8004scan.io/agents/{config['network']}/{agent_id}")


def cmd_whoami(network: Optional[str] = None):
    """Check if your wallet has an on-chain identity."""
    w3, config = get_web3("read", network)
    
    # Get address from env
    address = os.environ.get("CHAOSCHAIN_ADDRESS")
    private_key = os.environ.get("CHAOSCHAIN_PRIVATE_KEY")
    
    if private_key and not address:
        from eth_account import Account
        account = Account.from_key(private_key)
        address = account.address
    
    if not address:
        print("❌ No wallet configured")
        print("")
        print("Set CHAOSCHAIN_ADDRESS or CHAOSCHAIN_PRIVATE_KEY in your OpenClaw config:")
        print("")
        print('  "skills": {')
        print('    "entries": {')
        print('      "chaoschain": {')
        print('        "env": {')
        print('          "CHAOSCHAIN_ADDRESS": "0x..."')
        print('        }')
        print('      }')
        print('    }')
        print('  }')
        return
    
    print(f"⛓️ Checking identity for: {address[:10]}...{address[-8:]}")
    print(f"   Network: {config['network'].upper()}")
    print("━" * 40)
    
    identity = w3.eth.contract(
        address=w3.to_checksum_address(config["identity_registry"]),
        abi=IDENTITY_ABI
    )
    
    try:
        balance = identity.functions.balanceOf(w3.to_checksum_address(address)).call()
        
        if balance > 0:
            print(f"✅ You have {balance} registered agent(s)")
            print("")
            print("Use /chaoschain reputation <your_agent_id> to see your scores.")
        else:
            print("❌ No registered agent found for this wallet")
            print("")
            print("Register with: /chaoschain register")
            print("(Requires CHAOSCHAIN_PRIVATE_KEY and ETH for gas)")
    except Exception as e:
        print(f"❌ Error checking identity: {e}")


def cmd_register(network: Optional[str] = None):
    """Register your agent on ERC-8004 (ON-CHAIN ACTION)."""
    # Get config FIRST to show which network
    w3, config = get_web3("register", network)
    
    print("⚠️  WARNING: ON-CHAIN TRANSACTION")
    print("━" * 40)
    
    # Extra warning for mainnet
    if config["network"] == "mainnet":
        print("🔴 MAINNET TRANSACTION - REAL ETH REQUIRED")
        print("")
        print("You are about to register on Ethereum Mainnet.")
        print("This costs real ETH and is permanent.")
        print("")
        print("For testing, use: /chaoschain register --network sepolia")
        print("")
    else:
        print(f"Network: {config['network'].upper()} (testnet)")
        print("")
    
    print("This command will:")
    print("  1. Submit a transaction to Ethereum")
    print("  2. Cost gas (~0.001 ETH)")
    print("  3. Register your agent permanently on ERC-8004")
    print("")
    
    private_key = os.environ.get("CHAOSCHAIN_PRIVATE_KEY")
    
    if not private_key:
        print("❌ CHAOSCHAIN_PRIVATE_KEY not set")
        print("")
        print("To register, add your private key to OpenClaw config:")
        print("")
        print('  "skills": {')
        print('    "entries": {')
        print('      "chaoschain": {')
        print('        "env": {')
        print('          "CHAOSCHAIN_PRIVATE_KEY": "0x...",')
        print(f'          "CHAOSCHAIN_NETWORK": "{config["network"]}"')
        print('        }')
        print('      }')
        print('    }')
        print('  }')
        return
    
    from eth_account import Account
    account = Account.from_key(private_key)
    address = account.address
    
    print(f"Wallet: {address[:10]}...{address[-8:]}")
    print(f"Network: {config['network'].upper()}")
    
    # Check balance
    balance = w3.eth.get_balance(address)
    balance_eth = w3.from_wei(balance, 'ether')
    print(f"Balance: {balance_eth:.6f} ETH")
    
    if balance_eth < 0.001:
        print("")
        print("❌ Insufficient ETH for gas")
        print(f"   Need at least 0.001 ETH, have {balance_eth:.6f} ETH")
        return
    
    # Check if already registered
    identity = w3.eth.contract(
        address=w3.to_checksum_address(config["identity_registry"]),
        abi=IDENTITY_ABI
    )
    
    existing = identity.functions.balanceOf(w3.to_checksum_address(address)).call()
    if existing > 0:
        print("")
        print(f"⚠️  You already have {existing} registered agent(s)")
        print("   Registration will create an additional agent ID.")
    
    print("")
    print("Proceeding with registration...")
    print("")
    
    # Build transaction
    agent_uri = json.dumps({
        "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
        "name": f"OpenClaw Agent",
        "description": "AI agent registered via ChaosChain OpenClaw skill",
        "active": True
    })
    
    try:
        # Get nonce
        nonce = w3.eth.get_transaction_count(address)
        
        # Build tx
        tx = identity.functions.register(agent_uri).build_transaction({
            'from': address,
            'nonce': nonce,
            'gas': 200000,
            'maxFeePerGas': w3.eth.gas_price * 2,
            'maxPriorityFeePerGas': w3.to_wei(1, 'gwei'),
            'chainId': config['chain_id']
        })
        
        # Sign and send
        signed = w3.eth.account.sign_transaction(tx, private_key)
        tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
        
        print(f"📤 Transaction sent: {tx_hash.hex()}")
        print(f"🔗 {config['explorer']}/tx/{tx_hash.hex()}")
        print("")
        print("Waiting for confirmation...")
        
        # Wait for receipt
        receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
        
        if receipt.status == 1:
            print("")
            print("✅ Registration successful!")
            print("")
            print("Your agent is now registered on ERC-8004.")
            print("Use /chaoschain whoami to see your agent ID.")
        else:
            print("")
            print("❌ Transaction failed")
            print(f"   Receipt: {receipt}")
    
    except Exception as e:
        print(f"❌ Registration failed: {e}")


def parse_network_flag(args: list) -> Tuple[list, Optional[str]]:
    """Parse --network flag from arguments."""
    network = None
    filtered_args = []
    
    i = 0
    while i < len(args):
        if args[i] == "--network" and i + 1 < len(args):
            network = args[i + 1].lower()
            network = NETWORK_ALIASES.get(network, network)
            if network not in CONTRACTS:
                print(f"❌ Unknown network: {network}")
                print("   Valid options: mainnet, sepolia, or SDK-style keys (e.g. base_mainnet)")
                sys.exit(1)
            i += 2
        elif args[i].startswith("--network="):
            network = args[i].split("=")[1].lower()
            network = NETWORK_ALIASES.get(network, network)
            if network not in CONTRACTS:
                print(f"❌ Unknown network: {network}")
                print("   Valid options: mainnet, sepolia, or SDK-style keys (e.g. base_mainnet)")
                sys.exit(1)
            i += 1
        else:
            filtered_args.append(args[i])
            i += 1
    
    return filtered_args, network


def main():
    """Main entry point."""
    if len(sys.argv) < 2:
        print("ChaosChain OpenClaw Skill")
        print("━" * 40)
        print("Commands:")
        print("  verify <agent_id>      - Check agent registration")
        print("  reputation <agent_id>  - View reputation scores")
        print("  whoami                 - Check your identity")
        print("  register               - Register on ERC-8004")
        print("")
        print("Options:")
        print("  --network <network>    - mainnet/sepolia or SDK-style key (e.g. base_mainnet)")
        print("")
        print("Network Defaults:")
        print("  • Read operations (verify, reputation): ethereum_mainnet")
        print("  • Write operations (register): ethereum_sepolia (safe default)")
        print("")
        print("Examples:")
        print("  python chaoschain_skill.py verify 450")
        print("  python chaoschain_skill.py verify 450 --network sepolia")
        print("  python chaoschain_skill.py register --network sepolia")
        print("  python chaoschain_skill.py register --network mainnet  # advanced")
        return
    
    # Parse --network flag
    args, network = parse_network_flag(sys.argv[1:])
    
    if not args:
        print("No command specified. Use: verify, reputation, whoami, or register")
        return
    
    command = args[0].lower()
    
    if command == "verify":
        if len(args) < 2:
            print("Usage: verify <agent_id_or_address> [--network <network_key>]")
            return
        cmd_verify(args[1], network)
    
    elif command == "reputation":
        if len(args) < 2:
            print("Usage: reputation <agent_id_or_address> [--network <network_key>]")
            return
        cmd_reputation(args[1], network)
    
    elif command == "whoami":
        cmd_whoami(network)
    
    elif command == "register":
        cmd_register(network)
    
    else:
        print(f"Unknown command: {command}")
        print("Use: verify, reputation, whoami, or register")


if __name__ == "__main__":
    main()


path: scripts/whoami.py
content: #!/usr/bin/env python3
"""
Wrapper for whoami command - uses venv with web3 installed

OpenClaw expects separate scripts for each command.
This wrapper calls the main chaoschain_skill.py with the 'whoami' command.
"""
import sys
import os
import subprocess

scripts_dir = os.path.dirname(os.path.abspath(__file__))
skill_dir = os.path.dirname(scripts_dir)
venv_python = os.path.join(skill_dir, ".venv", "bin", "python3")
main_script = os.path.join(scripts_dir, "chaoschain_skill.py")

if os.path.exists(venv_python):
    python_exe = venv_python
else:
    python_exe = sys.executable

args = [python_exe, main_script, "whoami"] + sys.argv[1:]
sys.exit(subprocess.call(args))


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)