Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Register AI agents on-chain using the ERC-8004 Trustless Agents standard. Manage agent identity as NFTs, build reputation through feedback, and request third-party validation.
.claude/skills/internet-court-starknet-identity/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 77% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 59% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 52% | 0% |
Register and manage AI agent identities on Starknet using the ERC-8004 standard.
starknet and connect a funded account to the target ERC-8004 registry deployment.ERC-8004 defines three interconnected on-chain registries for AI agents:
Reference implementation: erc8004-cairo
bashnpm install starknet
typescriptimport { Account, RpcProvider, Contract, CallData } from "starknet"; const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC_URL }); const account = new Account({ provider, address, signer: privateKey }); const identityRegistry = new Contract({ abi: identityRegistryAbi, address: registryAddress, providerOrAccount: account, }); // Register with metadata const metadata = [ { key: "agentName", value: "MyTradingAgent" }, { key: "agentType", value: "defi-trader" }, { key: "version", value: "1.0.0" }, { key: "model", value: "claude-opus-4-5" }, { key: "status", value: "active" }, ]; const tokenUri = "ipfs://QmYourAgentSpecHash"; // IPFS link to full agent spec const { transaction_hash } = await account.execute({ contractAddress: registryAddress, entrypoint: "register_with_metadata", calldata: CallData.compile({ token_uri: tokenUri, metadata: metadata, }), }); const receipt = await account.waitForTransaction(transaction_hash); // Parse agent_id from events
typescript// Check if agent exists const exists = await identityRegistry.agent_exists(agentId); // Get total registered agents const totalAgents = await identityRegistry.total_agents(); // Get agent metadata const name = await identityRegistry.get_metadata(agentId, "agentName"); const agentType = await identityRegistry.get_metadata(agentId, "agentType"); // Get agent owner (ERC-721) const owner = await identityRegistry.owner_of(agentId);
typescript// Only the agent owner can update metadata await account.execute({ contractAddress: registryAddress, entrypoint: "set_metadata", calldata: CallData.compile({ agent_id: agentId, key: "status", value: "upgraded", }), });
The reputation system uses a cryptographic authorization flow:
typescript// Step 1: Agent owner creates authorization const feedbackAuth = { agent_id: agentId, client_address: clientAddress, index_limit: 10, // Max feedback entries allowed expiry: Math.floor(Date.now() / 1000) + 3600, // 1 hour chain_id: chainId, identity_registry: registryAddress, signer_address: ownerAddress, }; // Sign the authorization (using agent owner's account) const messageHash = computePoseidonHash(feedbackAuth); // EIP-712 style const signature = await ownerAccount.signMessage(messageHash); // Step 2: Client submits feedback await clientAccount.execute({ contractAddress: reputationRegistryAddress, entrypoint: "give_feedback", calldata: CallData.compile({ agent_id: agentId, score: 85, // 0-100 tag1: encodedTag("reliability"), tag2: encodedTag("speed"), fileuri: "", filehash: 0, feedback_auth: feedbackAuth, signature: signature, }), });
typescriptconst reputationRegistry = new Contract({ abi: reputationAbi, address: reputationAddress, providerOrAccount: provider, }); // Get summary for an agent (count + average score) const [count, avgScore] = await reputationRegistry.get_summary( agentId, [], // all client addresses (or filter specific ones) 0, // tag1 filter (0 = all) 0, // tag2 filter (0 = all) ); // Read specific feedback const [score, tag1, tag2, isRevoked] = await reputationRegistry.read_feedback( agentId, clientAddress, feedbackIndex, ); // Get all clients who gave feedback const clients = await reputationRegistry.get_clients(agentId);
typescriptconst validationRegistry = new Contract({ abi: validationAbi, address: validationAddress, providerOrAccount: account, }); // Agent owner requests validation from a specific validator await account.execute({ contractAddress: validationAddress, entrypoint: "validation_request", calldata: CallData.compile({ validator_address: validatorAddress, agent_id: agentId, request_uri: "ipfs://QmValidationRequestDetails", request_hash: 0, // Auto-generated if 0 }), });
typescript// Validator responds to the request await validatorAccount.execute({ contractAddress: validationAddress, entrypoint: "validation_response", calldata: CallData.compile({ request_hash: requestHash, response: 92, // Score 0-100 response_uri: "ipfs://QmValidationReport", response_hash: reportHash, tag: encodedTag("performance"), }), });
typescript// Get validation summary const [validationCount, avgValidationScore] = await validationRegistry.get_summary( agentId, [], // all validators 0, // tag filter ); // Get specific validation const [validator, agentId_, response, tag, lastUpdate] = await validationRegistry.get_validation_status(requestHash);
Combine on-chain identity with A2A Agent Cards for discoverability:
json{ "name": "MyTradingAgent", "description": "Autonomous DeFi trading agent on Starknet", "url": "https://my-agent.example.com", "provider": { "organization": "MyOrg" }, "version": "1.0.0", "capabilities": { "streaming": true, "pushNotifications": false }, "skills": [ { "id": "starknet-swap", "name": "Token Swap", "description": "Execute token swaps on Starknet via avnu" } ], "defaultInputModes": ["text/plain"], "defaultOutputModes": ["text/plain"], "authentication": { "schemes": ["bearer"] }, "starknetIdentity": { "registryAddress": "0x...", "agentId": 42, "reputationScore": 85, "validationCount": 3 } }
Serve at /.well-known/agent.json for A2A discovery.
| Key | Description | Example | |-----|-------------|---------| | agentName | Display name | "MyTradingAgent" | | agentType | Category | "defi-trader", "nft-curator", "data-analyst" | | version | Semantic version | "1.0.0" | | model | LLM model used | "claude-opus-4-5", "gpt-4o" | | status | Current status | "active", "paused", "deprecated" | | framework | Agent framework | "daydreams", "openclaw", "langchain" | | capabilities | Comma-separated | "swap,stake,lend" | | a2aEndpoint | Agent Card URL | "https://agent.example.com" | | moltbookId | MoltBook agent ID | "agent_abc123" |
| Code | Meaning | Likely causes | Recovery | User-facing message | |-----|---------|---------------|----------|---------------------| | REGISTRATION_FAILED | Initial ERC-8004 registration or metadata write reverted. | Missing fees, duplicate registration, invalid calldata, or stale contract state. | Retry after checking wallet balance, contract state, and constructor/registration inputs. | "Registration failed. Check wallet state and retry." | | AUTHORIZATION_DENIED | Ownership or feedback authorization proof was rejected. | Wrong signer, bad nonce, missing permission, or outdated owner state. | Re-fetch owner/nonce state, verify permissions, and re-sign with the current account. | "Authorization denied. Verify signer permissions and retry." | | VALIDATION_TIMEOUT | Off-chain validation or watcher flow did not complete before the deadline. | Slow relayer, RPC degradation, or downstream A2A service lag. | Retry with a longer timeout after checking chain health, relayer status, and RPC latency. | "Validation timed out. Retry after checking network health." | | SIGNATURE_EXPIRED | Signed payload expired before submission. | Expiry window too short or user approval arrived too late. | Generate a fresh signature with a new expiry and resubmit immediately. | "Signature expired. Re-sign and submit again." | | OWNER_TRANSFERRED | Agent NFT ownership changed while a write or feedback flow was in flight. | NFT transfer, marketplace sale, or custodial wallet rotation. | Refresh ownership state, re-authorize with the new owner, and explain that reputation follows the NFT. | "Ownership changed. Refresh owner state and retry with the new owner." |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | fail→pass | 12,224 | 7,165 | -41% | 1 | 1 | 0% | 2,476 | 4,268 | +72% | 0 | 0 | — |
case-08 | fail→pass | 13,633 | 7,499 | -45% | 1 | 1 | 0% | 2,921 | 4,359 | +49% | 0 | 0 | — |
case-09 | fail→pass | 10,061 | 4,885 | -51% | 1 | 1 | 0% | 2,143 | 3,794 | +77% | 0 | 0 | — |
case-10 | pass→pass | 11,325 | 3,491 | -69% | 1 | 1 | 0% | 2,472 | 3,497 | +41% | 0 | 0 | — |
case-11 | fail→pass | 14,132 | 5,764 | -59% | 1 | 1 | 0% | 2,400 | 3,815 | +59% | 0 | 0 | — |
case-01 | fail→pass | 13,747 | 7,122 | -48% | 1 | 1 | 0% | 2,810 | 4,270 | +52% | 0 | 0 | — |
case-12 | pass→pass | 11,351 | 3,780 | -67% | 1 | 1 | 0% | 1,872 | 3,413 | +82% | 0 | 0 | — |
case-02 | fail→pass | 14,945 | 15,631 | +5% | 1 | 1 | 0% | 3,316 | 6,526 | +97% | 0 | 0 | — |
case-03 | fail→pass | 9,925 | 7,205 | -27% | 1 | 1 | 0% | 2,046 | 4,395 | +115% | 0 | 0 | — |
case-04 | fail→pass | 10,752 | 7,234 | -33% | 1 | 1 | 0% | 2,212 | 4,280 | +93% | 0 | 0 | — |
case-05 | pass→pass | 10,943 | 6,066 | -45% | 1 | 1 | 0% | 2,335 | 4,117 | +76% | 0 | 0 | — |
case-06 | fail→pass | 12,990 | 7,917 | -39% | 1 | 1 | 0% | 2,586 | 4,371 | +69% | 0 | 0 | — |
case-13 | fail→pass | 12,476 | 2,169 | -83% | 1 | 1 | 0% | 2,147 | 3,132 | +46% | 0 | 0 | — |
case-14 | pass→pass | 6,138 | 1,754 | -71% | 1 | 1 | 0% | 1,019 | 3,031 | +197% | 0 | 0 | — |
case-15 | pass→pass | 12,117 | 3,664 | -70% | 1 | 1 | 0% | 1,985 | 3,301 | +66% | 0 | 0 | — |
case-16 | pass→pass | 14,678 | 6,771 | -54% | 1 | 1 | 0% | 2,433 | 3,968 | +63% | 0 | 0 | — |
case-17 | pass→pass | 12,650 | 3,574 | -72% | 1 | 1 | 0% | 2,540 | 3,497 | +38% | 0 | 0 | — |
case-18 | pass→pass | 6,058 | 2,118 | -65% | 1 | 1 | 0% | 1,048 | 3,097 | +196% | 0 | 0 | — |
case-19 | pass→pass | 9,013 | 4,814 | -47% | 1 | 1 | 0% | 2,042 | 3,868 | +89% | 0 | 0 | — |
case-20 | fail→fail | 21,005 | 13,438 | -36% | 1 | 1 | 0% | 5,004 | 6,181 | +24% | 0 | 0 | — |
case-21 | fail→fail | 11,122 | 6,046 | -46% | 1 | 1 | 0% | 2,533 | 4,064 | +60% | 0 | 0 | — |
case-22 | pass→pass | 8,616 | 7,656 | -11% | 1 | 1 | 0% | 836 | 3,440 | +311% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +45 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.