Verify a Report
Every WalletGuard audit is cryptographically signed and attested on-chain via EAS on Base. You do not need to trust our API; you can verify any report yourself.
EIP-712 Signature Scheme
Reports are signed using EIP-712 typed-data with a deterministic domain and a single primary type (AuditReport). The signature covers everything that matters: the contract, the chain, the report hash, the score, and the timestamp.
{
"name": "WalletGuard",
"version": "1",
"chainId": 8453,
"verifyingContract": "0x4200000000000000000000000000000000000021"
}{
"AuditReport": [
{ "name": "contractAddress", "type": "address" },
{ "name": "chainId", "type": "uint256" },
{ "name": "reportHash", "type": "bytes32" },
{ "name": "riskScore", "type": "uint256" },
{ "name": "findingCount", "type": "uint256" },
{ "name": "timestamp", "type": "uint256" },
{ "name": "engineVersion", "type": "string" },
{ "name": "keyVersion", "type": "uint8" }
]
}EAS Attestation on Base
Every audit report is attested on-chain via the Ethereum Attestation Service on Base L2. The attestation includes the contract address, chain id, report hash, risk score, and the prompt composite hash that was active when the audit ran.
EAS is a predeploy contract on Base at 0x4200000000000000000000000000000000000021. Each attestation costs less than one cent and is indexed by EASScan.
Manual Verification
The easiest way to check a report is the hosted tool at /verify. Paste the report JSON and it recovers the signer address, checks the signature against the known key, and confirms the EAS attestation exists on Base and has not been revoked.
Programmatic Verification (API)
POST the report fields and signature to /api/verify. The endpoint is public; no authentication required. Response includes the recovered signer address and whether it matches the known WalletGuard signer for that key version.
curl -X POST https://walletguard.ai/api/verify \
-H "Content-Type: application/json" \
-d '{
"contractAddress": "0x1234...abcd",
"chainId": 8453,
"reportHash": "0x8b1a9953c4611296a827abf8c47804d7...",
"riskScore": 7,
"findingCount": 3,
"timestamp": 1712534400,
"engineVersion": "v1.3.0",
"keyVersion": 1,
"signature": "0xabcdef..."
}'Programmatic Verification (viem)
Recover the signer yourself with viem's recoverTypedDataAddress. Use the domain and types exactly as shown above.
import { recoverTypedDataAddress } from "viem";
const DOMAIN = {
name: "WalletGuard",
version: "1",
chainId: 8453,
verifyingContract: "0x4200000000000000000000000000000000000021",
} as const;
const TYPES = {
AuditReport: [
{ name: "contractAddress", type: "address" },
{ name: "chainId", type: "uint256" },
{ name: "reportHash", type: "bytes32" },
{ name: "riskScore", type: "uint256" },
{ name: "findingCount", type: "uint256" },
{ name: "timestamp", type: "uint256" },
{ name: "engineVersion", type: "string" },
{ name: "keyVersion", type: "uint8" },
],
} as const;
// Recover the signer address from a report signature
const signer = await recoverTypedDataAddress({
domain: DOMAIN,
types: TYPES,
primaryType: "AuditReport",
message: {
contractAddress: report.contractAddress,
chainId: BigInt(report.chainId),
reportHash: report.reportHash,
riskScore: BigInt(report.riskScore),
findingCount: BigInt(report.findingCount),
timestamp: BigInt(report.timestamp),
engineVersion: report.engineVersion,
keyVersion: report.keyVersion,
},
signature: report.signature,
});
console.log("Recovered signer:", signer);Key Rotation
Every signature includes a keyVersion. When we rotate the signing key, the version increments and the new public address is published. Historical reports remain verifiable under their original key version.
