Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions erc-8004/scripts/bridge-to-mainnet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@
# Usage: ./bridge-to-mainnet.sh <amount-in-eth>
# Example: ./bridge-to-mainnet.sh 0.01

set -e
set -euo pipefail

# Require Bankr CLI
if ! command -v bankr >/dev/null 2>&1; then
echo "Bankr CLI not found. Install with: bun install -g @bankr/cli" >&2
echo "Error: Bankr CLI not found. Install with: bun install -g @bankr/cli" >&2
exit 1
fi

AMOUNT="${1:?Usage: bridge-to-mainnet.sh <amount-in-eth>}"

# Validate amount is a positive number
if ! echo "$AMOUNT" | grep -qE '^[0-9]+(\.[0-9]+)?$' || [ "$(echo "$AMOUNT <= 0" | bc -l)" = "1" ]; then
echo "Error: amount must be a positive number (e.g. 0.01)" >&2
exit 1
fi

echo "=== Bridging ETH to Mainnet ===" >&2
echo "Amount: $AMOUNT ETH" >&2
echo "From: Base" >&2
Expand Down
167 changes: 167 additions & 0 deletions erc-8004/scripts/capabilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* ERC-8004 Agent Capabilities
*
* Defines well-known capability identifiers and provides a validation
* layer that prevents agents from claiming arbitrary unverified capabilities.
*
* Capability Policy:
* Capabilities are self-declared strings in the registration metadata.
* Since there is currently no on-chain proof system for capability
* verification, this module:
* 1. Restricts claims to a well-known allowlist of recognized capability IDs
* 2. Flags unrecognized claims so consumers can treat them with lower trust
* 3. Documents what each capability means so consumers can verify behavior
*
* Future: capability proofs or on-chain demonstrations should be implemented
* in the ERC-8004 Validation Registry before high-stakes trust decisions
* are made based on capability claims alone.
*/

/**
* Well-known capability identifiers for ERC-8004 agents.
* Only these strings are recognized as verified claim types.
* Claims outside this set are flagged as UNVERIFIED.
*/
export const KNOWN_CAPABILITIES = {
/** Agent exposes an Agent-to-Agent (A2A) endpoint */
A2A: "a2a",
/** Agent supports the Model Context Protocol (MCP) */
MCP: "mcp",
/** Agent accepts x402 micropayments */
X402: "x402",
/** Agent has a resolvable ENS name */
ENS: "ens",
/** Agent has on-chain reputation signals in ERC-8004 Reputation Registry */
REPUTATION: "reputation",
/** Agent exposes a public web interface */
WEB: "web",
} as const;

export type KnownCapability = (typeof KNOWN_CAPABILITIES)[keyof typeof KNOWN_CAPABILITIES];

const KNOWN_SET = new Set<string>(Object.values(KNOWN_CAPABILITIES));

export interface CapabilityEntry {
id: string;
/** true = recognized well-known capability; false = unverified/unknown claim */
verified: boolean;
/** Human-readable description for known capabilities */
description?: string;
}

const CAPABILITY_DESCRIPTIONS: Record<string, string> = {
[KNOWN_CAPABILITIES.A2A]:
"Agent exposes an Agent-to-Agent (A2A) compatible endpoint for machine-to-machine communication.",
[KNOWN_CAPABILITIES.MCP]:
"Agent implements the Model Context Protocol (MCP) for tool/resource exposure.",
[KNOWN_CAPABILITIES.X402]:
"Agent accepts x402 HTTP micropayments for metered service access.",
[KNOWN_CAPABILITIES.ENS]:
"Agent has a verified ENS primary name resolving to its registered address.",
[KNOWN_CAPABILITIES.REPUTATION]:
"Agent has on-chain reputation signals recorded in the ERC-8004 Reputation Registry.",
[KNOWN_CAPABILITIES.WEB]:
"Agent exposes a public web interface for human interaction.",
};

/**
* Validate a list of capability claims from agent metadata.
*
* Recognized claims are marked verified:true.
* Unrecognized claims are marked verified:false and should be treated
* with skepticism β€” the agent is claiming something we cannot validate.
*
* @param claims - Raw capability strings from agent metadata
* @returns Annotated capability entries
*/
export function validateCapabilities(claims: string[]): CapabilityEntry[] {
if (!Array.isArray(claims)) {
return [];
}

return claims
.filter((c): c is string => typeof c === "string" && c.trim() !== "")
.map((c) => {
const normalized = c.trim().toLowerCase();
const isKnown = KNOWN_SET.has(normalized);
return {
id: normalized,
verified: isKnown,
description: isKnown ? CAPABILITY_DESCRIPTIONS[normalized] : undefined,
};
});
}

/**
* Filter to only the capabilities that can be partially verified by
* cross-referencing the agent's service list in its metadata.
*
* For example:
* - "a2a" claim is plausible only if an A2A service endpoint is present
* - "mcp" claim is plausible only if an MCP service endpoint is present
* - "x402" claim is plausible only if x402Support=true in metadata
*
* This is not cryptographic proof β€” it only checks internal consistency.
* True verification requires an external oracle or on-chain demonstration.
*
* @param claims - Capability claims from metadata
* @param metadata - Full parsed metadata object for cross-reference
* @returns Capability entries with consistency check results
*/
export function crossCheckCapabilities(
claims: string[],
metadata: {
services?: Array<{ name: string; endpoint?: string }>;
x402Support?: boolean;
}
): CapabilityEntry[] {
const entries = validateCapabilities(claims);

const serviceNames = new Set(
(metadata.services ?? []).map((s) => s.name.toLowerCase())
);

return entries.map((entry) => {
if (!entry.verified) return entry;

// Cross-check internal consistency
let consistent = true;
let note: string | undefined;

switch (entry.id) {
case KNOWN_CAPABILITIES.A2A:
consistent = serviceNames.has("a2a");
note = consistent
? undefined
: "Claims A2A but no A2A service endpoint found in metadata";
break;
case KNOWN_CAPABILITIES.MCP:
consistent = serviceNames.has("mcp");
note = consistent
? undefined
: "Claims MCP but no MCP service endpoint found in metadata";
break;
case KNOWN_CAPABILITIES.X402:
consistent = metadata.x402Support === true;
note = consistent
? undefined
: "Claims x402 but x402Support is not true in metadata";
break;
case KNOWN_CAPABILITIES.WEB:
consistent = serviceNames.has("web");
note = consistent
? undefined
: "Claims web but no web service endpoint found in metadata";
break;
// ENS and REPUTATION require external on-chain lookup to verify
default:
break;
}

return {
...entry,
verified: consistent,
description: note ?? entry.description,
};
});
}
69 changes: 42 additions & 27 deletions erc-8004/scripts/create-registration.sh
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
#!/bin/bash
# ERC-8004 - Create agent registration JSON file
# Usage: ./create-registration.sh [output-file]
#
#
# Environment variables:
# AGENT_NAME - Agent display name (default: uses wallet address)
# AGENT_NAME - Agent display name (default: "AI Agent")
# AGENT_DESCRIPTION - Agent description
# AGENT_IMAGE - Avatar URL
# AGENT_WEBSITE - Agent website
# AGENT_IMAGE - Avatar URL
# AGENT_WEBSITE - Agent website
# AGENT_A2A_ENDPOINT - A2A agent card URL
# AGENT_MCP_ENDPOINT - MCP server endpoint
# AGENT_ENS - ENS name
# X402_SUPPORT - Enable x402 payments (true/false, default: false)
# AGENT_ENS - ENS name
# X402_SUPPORT - Enable x402 payments (true/false, default: false)

set -e
set -euo pipefail

OUTPUT_FILE="${1:-/tmp/agent-registration.json}"

# Default values
# Defaults
NAME="${AGENT_NAME:-AI Agent}"
DESCRIPTION="${AGENT_DESCRIPTION:-An autonomous AI agent registered on ERC-8004}"
IMAGE="${AGENT_IMAGE:-}"
Expand All @@ -26,44 +26,59 @@ MCP_ENDPOINT="${AGENT_MCP_ENDPOINT:-}"
ENS="${AGENT_ENS:-}"
X402="${X402_SUPPORT:-false}"

# Validate X402 value
if [[ "$X402" != "true" && "$X402" != "false" ]]; then
echo "Error: X402_SUPPORT must be 'true' or 'false'" >&2
exit 1
fi

echo "=== Creating Registration File ===" >&2
echo "Name: $NAME" >&2
echo "Description: $DESCRIPTION" >&2
echo "Output: $OUTPUT_FILE" >&2

# Build services array
# Build services array safely using jq β€” no shell injection risk
SERVICES="[]"

if [ -n "$WEBSITE" ]; then
SERVICES=$(echo "$SERVICES" | jq --arg url "$WEBSITE" '. + [{"name": "web", "endpoint": $url}]')
SERVICES=$(jq -n --argjson arr "$SERVICES" --arg url "$WEBSITE" \
'$arr + [{"name": "web", "endpoint": $url}]')
fi

if [ -n "$A2A_ENDPOINT" ]; then
SERVICES=$(echo "$SERVICES" | jq --arg url "$A2A_ENDPOINT" '. + [{"name": "A2A", "endpoint": $url, "version": "0.3.0"}]')
SERVICES=$(jq -n --argjson arr "$SERVICES" --arg url "$A2A_ENDPOINT" \
'$arr + [{"name": "A2A", "endpoint": $url, "version": "0.3.0"}]')
fi

if [ -n "$MCP_ENDPOINT" ]; then
SERVICES=$(echo "$SERVICES" | jq --arg url "$MCP_ENDPOINT" '. + [{"name": "MCP", "endpoint": $url, "version": "2025-06-18"}]')
SERVICES=$(jq -n --argjson arr "$SERVICES" --arg url "$MCP_ENDPOINT" \
'$arr + [{"name": "MCP", "endpoint": $url, "version": "2025-06-18"}]')
fi

if [ -n "$ENS" ]; then
SERVICES=$(echo "$SERVICES" | jq --arg ens "$ENS" '. + [{"name": "ENS", "endpoint": $ens, "version": "v1"}]')
SERVICES=$(jq -n --argjson arr "$SERVICES" --arg ens "$ENS" \
'$arr + [{"name": "ENS", "endpoint": $ens, "version": "v1"}]')
fi

# Create registration file
cat > "$OUTPUT_FILE" << EOF
{
"type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
"name": "$NAME",
"description": "$DESCRIPTION",
"image": "$IMAGE",
"services": $SERVICES,
"x402Support": $X402,
"active": true,
"registrations": [],
"supportedTrust": ["reputation"]
}
EOF
# Build the full JSON safely using jq β€” never raw string interpolation
jq -n \
--arg type "https://eips.ethereum.org/EIPS/eip-8004#registration-v1" \
--arg name "$NAME" \
--arg description "$DESCRIPTION" \
--arg image "$IMAGE" \
--argjson services "$SERVICES" \
--argjson x402 "$X402" \
'{
"type": $type,
"name": $name,
"description": $description,
"image": $image,
"services": $services,
"x402Support": $x402,
"active": true,
"registrations": [],
"supportedTrust": ["reputation"]
}' > "$OUTPUT_FILE"

echo "=== SUCCESS ===" >&2
echo "Created: $OUTPUT_FILE" >&2
Expand Down
Loading