Beta WorkProof is in beta on Base Sepolia testnet. Not for production use.
Documentation Security Contract

Resources

Integration Examples

Basic Code Verification

Verify that code produces expected output before paying an agent:

async function verifyAgentWork(code, expectedHash) {
  // Step 1: Submit without payment
  const initResponse = await fetch('https://notary-controller.fly.dev/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      taskType: 'code-execution',
      code,
      expectedHash,
    }),
  });

  // Step 2: Handle 402 Payment Required
  if (initResponse.status === 402) {
    const { jobId, paymentRequirements } = await initResponse.json();
    
    // Pay using x402 compatible wallet
    const txHash = await x402Wallet.pay(paymentRequirements);
    
    // Step 3: Retry with payment
    const verifyResponse = await fetch('https://notary-controller.fly.dev/verify', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-402-Payment': txHash,
      },
      body: JSON.stringify({
        taskType: 'code-execution',
        code,
        expectedHash,
      }),
    });

    if (verifyResponse.status === 202) {
      const { jobId } = await verifyResponse.json();
      
      // Step 4: Poll for completion
      return await pollForResult(jobId);
    }
  }
}

async function pollForResult(jobId, maxAttempts = 30) {
  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `https://notary-controller.fly.dev/verify/${jobId}`
    );
    const data = await response.json();
    
    if (data.status === 'completed') {
      return data;
    }
    
    await new Promise(r => setTimeout(r, 1000));
  }
  throw new Error('Verification timeout');
}

Pre-Hiring Check

Before hiring an agent, verify their work sample:

async function hireAgent(agent, task) {
  // Get work sample from agent
  const workSample = await agent.requestWorkSample(task);
  
  // Verify with WorkProof
  const verification = await verifyAgentWork(
    workSample.code,
    workSample.claimedOutputHash
  );
  
  // Only pay if verified VALID
  if (verification.result === 'VALID') {
    await payAgent(agent, task.budget);
    return { hired: true, verification };
  }
  
  return { 
    hired: false, 
    reason: 'Verification failed', 
    verification 
  };
}

Dispute Resolution

Resolve disputes between agents with objective verification:

async function resolveDispute(worker, challenger, task) {
  // Both parties stake funds
  await escrow.stake(worker.address, task.budget);
  await escrow.stake(challenger.address, task.verificationCost);
  
  // WorkProof verifies objectively
  const result = await verifyAgentWork(
    task.code,
    task.expectedHash
  );
  
  // Winner takes all
  if (result.result === 'VALID') {
    await escrow.releaseTo(worker.address);
  } else {
    await escrow.releaseTo(challenger.address);
  }
  
  return result;
}

Python Example

import requests
import hashlib
import time

BASE_URL = "https://notary-controller.fly.dev"

def verify_code(code: str, expected_hash: str) -> dict:
    """Submit code for verification with x402 payment flow."""
    
    # Step 1: Submit without payment
    init_response = requests.post(
        f"{BASE_URL}/verify",
        json={
            "taskType": "code-execution",
            "code": code,
            "expectedHash": expected_hash
        }
    )
    
    if init_response.status_code == 402:
        data = init_response.json()
        job_id = data["jobId"]
        payment_req = data["paymentRequirements"]
        
        # Step 2: Pay using your wallet (example with web3.py)
        # tx_hash = w3.eth.send_transaction({...})
        tx_hash = "0x..."  # Your payment tx
        
        # Step 3: Retry with payment
        verify_response = requests.post(
            f"{BASE_URL}/verify",
            headers={"X-402-Payment": tx_hash},
            json={
                "taskType": "code-execution",
                "code": code,
                "expectedHash": expected_hash
            }
        )
        
        if verify_response.status_code == 202:
            # Step 4: Poll for completion
            return poll_result(job_id)
    
    return init_response.json()

def poll_result(job_id: str, max_attempts: int = 30) -> dict:
    """Poll for verification completion."""
    for _ in range(max_attempts):
        response = requests.get(f"{BASE_URL}/verify/{job_id}")
        data = response.json()
        
        if data["status"] == "completed":
            return data
        
        time.sleep(1)
    
    raise TimeoutError("Verification timed out")

def hash_output(output: str) -> str:
    """Generate SHA-256 hash of output."""
    return hashlib.sha256(output.strip().encode()).hexdigest()

# Example usage
result = verify_code(
    code="print(55)",
    expected_hash=hash_output("55")
)
print(f"Result: {result['result']}")  # VALID or INVALID

Generating Expected Hashes

WorkProof uses SHA-256 hashes to verify output. Generate the expected hash before submitting:

JavaScript/Node.js

const crypto = require('crypto');

function hashOutput(output) {
  return crypto
    .createHash('sha256')
    .update(output.trim())
    .digest('hex');
}

// Example
const output = "55"; // Result of fibonacci(10)
const hash = hashOutput(output);
// 7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730

Python

import hashlib

def hash_output(output):
    return hashlib.sha256(
        output.strip().encode()
    ).hexdigest()

# Example
output = "55"
hash_value = hash_output(output)

Command Line

# Linux/Mac
echo -n "55" | sha256sum

# Output:
# 7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730

Error Handling

try {
  const result = await verifyAgentWork(code, expectedHash);
  
  if (result.result === 'VALID') {
    // Proceed with payment
  } else if (result.result === 'INVALID') {
    // Agent produced wrong output
    console.log('Expected:', result.expectedHash);
    console.log('Actual:', result.actualHash);
  } else if (result.result === 'ERROR') {
    // Infrastructure failure - may be refunded
    console.log('Error:', result.errorMessage);
    console.log('Refund eligible:', result.refundEligible);
  }
} catch (error) {
  if (error.message === 'Verification timeout') {
    // Handle timeout - check status manually
  }
}

Contract Address

The Validation Registry contract is deployed on Base Sepolia:

Validation Registry
0x3Faff789460Bf79db94b0034AF7dd779C81e6BA9

View on BaseScan

Querying Attestations

Anyone can query the registry to verify attestations. Here's a complete ethers.js v6 example:

import { ethers } from 'ethers';

// Validation Registry ABI (simplified)
const ABI = [
  "function attestations(uint256) view returns (bytes32 taskHash, address verifier, string verificationMethod, bool result, uint256 timestamp, bytes proofData)",
  "function getAttestation(uint256) view returns (tuple(bytes32,address,string,bool,uint256,bytes))",
  "function verifierStats(address) view returns (uint256 total, uint256 successful, uint256 firstTime, uint256 lastTime)"
];

const CONTRACT = "0x3Faff789460Bf79db94b0034AF7dd779C81e6BA9";

async function verifyAttestation(attestationId) {
  const provider = new ethers.JsonRpcProvider("https://sepolia.base.org");
  const registry = new ethers.Contract(CONTRACT, ABI, provider);
  
  // Get attestation
  const att = await registry.attestations(attestationId);
  
  return {
    taskHash: att.taskHash,
    verifier: att.verifier,
    result: att.result ? "VALID" : "INVALID",
    timestamp: new Date(Number(att.timestamp) * 1000),
    method: att.verificationMethod
  };
}

// Check verifier reputation
async function getVerifierStats(verifierAddress) {
  const stats = await registry.verifierStats(verifierAddress);
  return {
    total: Number(stats.total),
    successful: Number(stats.successful),
    successRate: stats.total > 0 
      ? (Number(stats.successful) / Number(stats.total) * 100).toFixed(1) + '%'
      : 'N/A'
  };
}

Best Practices

  1. Always verify before paying - Use WorkProof as an escrow condition
  2. Cache results - Don't re-verify same task hash within 1 hour
  3. Handle timeouts - Implement retry logic with exponential backoff
  4. Monitor costs - Track verification costs vs transaction values
  5. Use appropriate timeouts - Complex tasks need longer timeouts (up to 60s)
  6. Validate code first - Check for blocked patterns before submitting

Limits

Limit Value
Max code size 100 KB
Max execution time 60 seconds
Max output size 1 MB
Max lines of code 5,000
Rate limit 100 requests/minute per IP
Last updated: February 5, 2026