Skip to content

Step 3: Attest Compliance

Goal

Submit a compliance attestation to the Agent Registry. The on-chain grace period is 7 days (attestationGracePeriod = 7 days). If your agent does not attest within 7 days of its last attestation, it is no longer considered compliant by KYA checks (isRegisteredAndCompliant returns false).

Recommendation: Attest every 6 days (518,400 seconds) to maintain a 1-day safety buffer.

from agentenregister import AgentRegistry

registry = AgentRegistry(
    chain="base_sepolia",
    registry_address="0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23",
    private_key="0x_your_agent_private_key",
    relayer_url="https://relay.theagentregistry.org",
)

agent_id = 42  # Your agent's ID from registration
registry.attest(agent_id)
# Output: Compliance attested for agent 42
import { AgentRegistry } from "@agentenregister/sdk";

const registry = new AgentRegistry({
    chain: "base_sepolia",
    registryAddress: "0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23",
    privateKey: "0x_your_agent_private_key",
    relayerUrl: "https://relay.theagentregistry.org",
});

const agentId = 42; // Your agent's ID from registration
await registry.attest(agentId);
console.log(`Compliance attested for agent ${agentId}`);

Method B: Raw Implementation (No SDK)

from web3 import Web3
from eth_account import Account
from eth_account.messages import encode_structured_data
import requests
import json

# ── Configuration ─────────────────────────────────────────────
PRIVATE_KEY = "0x_your_agent_private_key"
RELAYER_URL = "https://relay.theagentregistry.org"
REGISTRY_ADDRESS = "0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23"
CHAIN_ID = 84532
AGENT_ID = 42

account = Account.from_key(PRIVATE_KEY)

# ── Get forwarder address and nonce ───────────────────────────
domain = requests.get(f"{RELAYER_URL}/domain").json()
FORWARDER_ADDRESS = domain["verifyingContract"]

nonce = int(
    requests.get(f"{RELAYER_URL}/nonce/{account.address}").json()["nonce"]
)

# ── Encode attestCompliance calldata ──────────────────────────
w3 = Web3()
ABI = json.loads(
    '[{"inputs":[{"name":"agentId","type":"uint256"}],'
    '"name":"attestCompliance","outputs":[],'
    '"stateMutability":"nonpayable","type":"function"}]'
)
contract = w3.eth.contract(address=REGISTRY_ADDRESS, abi=ABI)
calldata = contract.functions.attestCompliance(
    AGENT_ID
).build_transaction({"gas": 0, "gasPrice": 0, "nonce": 0})["data"]

# ── Sign EIP-712 typed data ───────────────────────────────────
typed_data = {
    "types": {
        "EIP712Domain": [
            {"name": "name", "type": "string"},
            {"name": "version", "type": "string"},
            {"name": "chainId", "type": "uint256"},
            {"name": "verifyingContract", "type": "address"},
        ],
        "ForwardRequest": [
            {"name": "from", "type": "address"},
            {"name": "to", "type": "address"},
            {"name": "value", "type": "uint256"},
            {"name": "gas", "type": "uint256"},
            {"name": "nonce", "type": "uint256"},
            {"name": "deadline", "type": "uint48"},
            {"name": "data", "type": "bytes"},
        ],
    },
    "primaryType": "ForwardRequest",
    "domain": {
        "name": "MinimalForwarder",
        "version": "1",
        "chainId": CHAIN_ID,
        "verifyingContract": FORWARDER_ADDRESS,
    },
    "message": {
        "from": account.address,
        "to": REGISTRY_ADDRESS,
        "value": 0,
        "gas": 200000,
        "nonce": nonce,
        "deadline": 0,
        "data": calldata,
    },
}

encoded = encode_structured_data(typed_data)
signed = account.sign_message(encoded)

# ── Submit to relayer ─────────────────────────────────────────
result = requests.post(f"{RELAYER_URL}/relay", json={
    "request": {
        "from": account.address,
        "to": REGISTRY_ADDRESS,
        "value": "0",
        "gas": "200000",
        "nonce": str(nonce),
        "deadline": "0",
        "data": calldata,
    },
    "signature": signed.signature.hex(),
}).json()

print(f"Attested! TX: {result['transactionHash']}")
import { ethers } from "ethers";

// ── Configuration ─────────────────────────────────────────────
const PRIVATE_KEY = "0x_your_agent_private_key";
const RELAYER_URL = "https://relay.theagentregistry.org";
const REGISTRY_ADDRESS = "0x2EFaB5B3BEf49E56a6Ce1dcB1A39EF63C312EA23";
const CHAIN_ID = 84532;
const AGENT_ID = 42;

const provider = new ethers.JsonRpcProvider("https://sepolia.base.org");
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

// ── Get forwarder address and nonce ───────────────────────────
const domainRes = await fetch(`${RELAYER_URL}/domain`);
const domain = await domainRes.json();
const FORWARDER_ADDRESS = domain.verifyingContract;

const nonceRes = await fetch(`${RELAYER_URL}/nonce/${wallet.address}`);
const nonce = (await nonceRes.json()).nonce;

// ── Encode attestCompliance calldata ──────────────────────────
const ABI = ["function attestCompliance(uint256 agentId)"];
const contract = new ethers.Contract(REGISTRY_ADDRESS, ABI, wallet);
const calldata = contract.interface.encodeFunctionData(
    "attestCompliance",
    [AGENT_ID]
);

// ── Sign EIP-712 typed data ───────────────────────────────────
const request = {
    from: wallet.address,
    to: REGISTRY_ADDRESS,
    value: "0",
    gas: "200000",
    nonce: String(nonce),
    deadline: "0",
    data: calldata,
};

const signature = await wallet.signTypedData(
    {
        name: "MinimalForwarder",
        version: "1",
        chainId: BigInt(CHAIN_ID),
        verifyingContract: FORWARDER_ADDRESS,
    },
    {
        ForwardRequest: [
            { name: "from", type: "address" },
            { name: "to", type: "address" },
            { name: "value", type: "uint256" },
            { name: "gas", type: "uint256" },
            { name: "nonce", type: "uint256" },
            { name: "deadline", type: "uint48" },
            { name: "data", type: "bytes" },
        ],
    },
    request,
);

// ── Submit to relayer ─────────────────────────────────────────
const relayRes = await fetch(`${RELAYER_URL}/relay`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ request, signature }),
});

const result = await relayRes.json();
console.log(`Attested! TX: ${result.transactionHash}`);

Scheduling

Set up a recurring timer to attest every 6 days. Here are common approaches:

Cron Job (Linux/macOS)

# Attest every 6 days (at midnight on day 1, 7, 13, 19, 25, 31)
0 0 */6 * * cd /path/to/agent && python -c "
from agentenregister import AgentRegistry
r = AgentRegistry.from_env()
r.attest(42)
"

In-Process Timer (Python)

import threading

ATTEST_INTERVAL = 6 * 24 * 3600  # 6 days in seconds

def attest_loop(registry, agent_id):
    """Background thread that attests compliance every 6 days."""
    while True:
        try:
            registry.attest(agent_id)
            print(f"Compliance attested for agent {agent_id}")
        except Exception as e:
            print(f"Attestation failed: {e}. Will retry next cycle.")
        threading.Event().wait(ATTEST_INTERVAL)

# Start background attestation
thread = threading.Thread(
    target=attest_loop,
    args=(registry, agent_id),
    daemon=True,
)
thread.start()

In-Process Timer (TypeScript)

const ATTEST_INTERVAL_MS = 6 * 24 * 3600 * 1000; // 6 days in ms

setInterval(async () => {
    try {
        await registry.attest(agentId);
        console.log(`Compliance attested for agent ${agentId}`);
    } catch (e) {
        console.error(`Attestation failed: ${e}. Will retry next cycle.`);
    }
}, ATTEST_INTERVAL_MS);

Check Attestation Status

Before attesting, you can check whether attestation is actually needed:

status = registry.get_compliance_status(agent_id)
print(f"Attestation current: {status['attestation_current']}")
print(f"Seconds since last:  {status['seconds_since_attestation']}")

if not status["attestation_current"]:
    registry.attest(agent_id)
const status = await registry.getComplianceStatus(agentId);
console.log(`Attestation current: ${status.attestationCurrent}`);
console.log(`Seconds since last:  ${status.secondsSinceAttestation}`);

if (!status.attestationCurrent) {
    await registry.attest(agentId);
}

Expected Output

Compliance attested for agent 42

Common Errors

Error Cause Resolution
"Agent not found" The agentId does not exist in the registry Verify your agent ID from registration
"Agent not active" Agent status is Suspended, Revoked, or Terminated Check status; suspended agents must be reactivated by a regulator
"Not authorized for this agent" The signing wallet is not the agent wallet, creator, or Haftungsperson Sign with the correct private key
"Invalid signature or nonce mismatch" Stale nonce or wrong chain ID Re-fetch nonce from /nonce/:address and re-sign

For the full error reference, see Errors.

Next Step

If your agent earns revenue, proceed to Step 4: Report Revenue. Otherwise, your compliance loop is complete -- just keep attesting every 6 days.