Skip to main content

Overview

The ZKScore Trust Registry contract emits events for all attestation lifecycle activities, schema registration, and trust score updates. These events enable real-time monitoring and off-chain indexing of the trust graph.
Events provide a complete audit trail of all trust-related activities and are essential for building trust analytics and reputation systems.

Attestation Events

Attested

Emitted when a new attestation is created.
event Attested(
    address indexed recipient,
    address indexed attester,
    bytes32 uid,
    bytes32 indexed schema
);
Parameters:
  • recipient (address indexed): Attestation recipient
  • attester (address indexed): Who created the attestation
  • uid (bytes32): Unique attestation identifier
  • schema (bytes32 indexed): Schema used
When Emitted:
  • When attest() is successfully called
  • During batch attestation operations
Example Usage:
// Listen for attestations
contract.on('Attested', (recipient, attester, uid, schema, event) => {
  console.log('New Attestation:');
  console.log(`  Recipient: ${recipient}`);
  console.log(`  Attester: ${attester}`);
  console.log(`  UID: ${uid}`);
  console.log(`  Schema: ${schema}`);
  console.log(`  Block: ${event.blockNumber}`);
});

// Filter for specific recipient
const recipientFilter = contract.filters.Attested('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
contract.on(recipientFilter, (recipient, attester, uid, schema) => {
  console.log(`Alice received attestation: ${uid}`);
});

// Get attestation history
async function getAttestationHistory(recipient) {
  const filter = contract.filters.Attested(recipient);
  const events = await contract.queryFilter(filter);
  
  return events.map(e => ({
    recipient: e.args.recipient,
    attester: e.args.attester,
    uid: e.args.uid,
    schema: e.args.schema,
    blockNumber: e.blockNumber,
    timestamp: e.args.timestamp
  }));
}

Revoked

Emitted when an attestation is revoked.
event Revoked(
    address indexed recipient,
    address indexed attester,
    bytes32 uid,
    bytes32 indexed schema
);
Parameters:
  • recipient (address indexed): Original attestation recipient
  • attester (address indexed): Who revoked the attestation
  • uid (bytes32): Attestation identifier
  • schema (bytes32 indexed): Schema used
When Emitted:
  • When revoke() is successfully called
  • During batch revocation operations
Example Usage:
// Monitor revocations
contract.on('Revoked', (recipient, attester, uid, schema, event) => {
  console.log(`Attestation ${uid} revoked by ${attester}`);
});

// Track revocations for specific schema
const schemaFilter = contract.filters.Revoked(null, null, null, schemaUID);
contract.on(schemaFilter, (recipient, attester, uid, schema) => {
  console.log(`Schema ${schema} attestation revoked: ${uid}`);
});

Schema Events

SchemaRegistered

Emitted when a new schema is registered.
event SchemaRegistered(
    bytes32 indexed uid,
    address indexed registerer
);
Parameters:
  • uid (bytes32 indexed): Schema unique identifier
  • registerer (address indexed): Who registered the schema
When Emitted:
  • When registerSchema() is successfully called
Example Usage:
// Monitor schema registrations
contract.on('SchemaRegistered', async (uid, registerer, event) => {
  console.log('New Schema Registered:');
  console.log(`  UID: ${uid}`);
  console.log(`  Registerer: ${registerer}`);
  
  // Get schema details
  const schema = await contract.getSchema(uid);
  console.log(`  Definition: ${schema.schema}`);
  console.log(`  Revocable: ${schema.revocable}`);
});

// Get all registered schemas
async function getAllSchemas() {
  const filter = contract.filters.SchemaRegistered();
  const events = await contract.queryFilter(filter);
  
  const schemas = await Promise.all(
    events.map(async e => {
      const schema = await contract.getSchema(e.args.uid);
      return {
        uid: e.args.uid,
        registerer: e.args.registerer,
        definition: schema.schema,
        revocable: schema.revocable,
        blockNumber: e.blockNumber
      };
    })
  );
  
  return schemas;
}

Trust Module Events

TrustModuleRegistered

Emitted when a trust evaluation module is registered.
event TrustModuleRegistered(
    address indexed module,
    string name
);
Parameters:
  • module (address indexed): Module contract address
  • name (string): Module name
When Emitted:
  • When a new trust module is registered

TrustScoreUpdated

Emitted when a user’s trust score changes.
event TrustScoreUpdated(
    address indexed subject,
    uint256 oldScore,
    uint256 newScore
);
Parameters:
  • subject (address indexed): User whose score changed
  • oldScore (uint256): Previous trust score
  • newScore (uint256): New trust score
When Emitted:
  • When trust score is recalculated
  • After attestation creation/revocation

Event Monitoring

Comprehensive Trust Monitor

class TrustEventMonitor {
  constructor(contract) {
    this.contract = contract;
    this.attestations = new Map();
    this.schemas = new Map();
    this.trustScores = new Map();
    this.setupListeners();
  }
  
  setupListeners() {
    // Monitor attestations
    this.contract.on('Attested', (recipient, attester, uid, schema, event) => {
      this.handleAttested(recipient, attester, uid, schema, event);
    });
    
    // Monitor revocations
    this.contract.on('Revoked', (recipient, attester, uid, schema, event) => {
      this.handleRevoked(recipient, attester, uid, schema, event);
    });
    
    // Monitor schemas
    this.contract.on('SchemaRegistered', (uid, registerer, event) => {
      this.handleSchemaRegistered(uid, registerer, event);
    });
    
    // Monitor trust scores
    this.contract.on('TrustScoreUpdated', (subject, oldScore, newScore, event) => {
      this.handleTrustScoreUpdated(subject, oldScore, newScore, event);
    });
  }
  
  handleAttested(recipient, attester, uid, schema, event) {
    const attestation = {
      recipient,
      attester,
      uid,
      schema,
      blockNumber: event.blockNumber,
      timestamp: event.args.timestamp
    };
    
    // Store attestation
    this.attestations.set(uid, attestation);
    
    // Update recipient's attestation list
    if (!this.recipientAttestations.has(recipient)) {
      this.recipientAttestations.set(recipient, []);
    }
    this.recipientAttestations.get(recipient).push(uid);
    
    console.log(`✅ New attestation: ${uid.slice(0, 10)}... for ${recipient.slice(0, 10)}...`);
    
    // Trigger analytics update
    this.updateAnalytics(recipient);
  }
  
  handleRevoked(recipient, attester, uid, schema, event) {
    const attestation = this.attestations.get(uid);
    if (attestation) {
      attestation.revoked = true;
      attestation.revokedAt = event.blockNumber;
    }
    
    console.log(`❌ Attestation revoked: ${uid.slice(0, 10)}...`);
    
    // Update analytics
    this.updateAnalytics(recipient);
  }
  
  handleSchemaRegistered(uid, registerer, event) {
    this.schemas.set(uid, {
      uid,
      registerer,
      blockNumber: event.blockNumber
    });
    
    console.log(`📋 New schema registered: ${uid.slice(0, 10)}...`);
  }
  
  handleTrustScoreUpdated(subject, oldScore, newScore, event) {
    this.trustScores.set(subject, {
      score: newScore.toString(),
      previousScore: oldScore.toString(),
      updatedAt: event.blockNumber
    });
    
    const change = newScore.sub(oldScore);
    const direction = change.isNegative() ? '📉' : '📈';
    
    console.log(`${direction} Trust score updated for ${subject.slice(0, 10)}...`);
    console.log(`  Old: ${oldScore.toString()}, New: ${newScore.toString()}`);
  }
  
  updateAnalytics(address) {
    // Calculate analytics
    const attestations = this.recipientAttestations.get(address) || [];
    const activeAttestations = attestations.filter(uid => {
      const att = this.attestations.get(uid);
      return att && !att.revoked;
    });
    
    console.log(`📊 ${address.slice(0, 10)}... has ${activeAttestations.length} active attestations`);
  }
  
  getStats() {
    return {
      totalAttestations: this.attestations.size,
      totalSchemas: this.schemas.size,
      trackedUsers: this.trustScores.size
    };
  }
  
  getRecipientStats(address) {
    const attestations = this.recipientAttestations.get(address) || [];
    const active = attestations.filter(uid => {
      const att = this.attestations.get(uid);
      return att && !att.revoked;
    });
    
    return {
      total: attestations.length,
      active: active.length,
      revoked: attestations.length - active.length,
      trustScore: this.trustScores.get(address)?.score || '0'
    };
  }
}

// Usage
const monitor = new TrustEventMonitor(contract);

// Get overall stats
console.log('Trust Registry Stats:', monitor.getStats());

// Get stats for specific user
const userStats = monitor.getRecipientStats('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
console.log('User Stats:', userStats);

Event Analytics

Trust Graph Analysis

async function analyzeTrustGraph() {
  const currentBlock = await provider.getBlockNumber();
  const startBlock = currentBlock - 100000;
  
  // Get all attestations
  const attestedFilter = contract.filters.Attested();
  const attestedEvents = await contract.queryFilter(attestedFilter, startBlock);
  
  // Get all revocations
  const revokedFilter = contract.filters.Revoked();
  const revokedEvents = await contract.queryFilter(revokedFilter, startBlock);
  
  const analytics = {
    totalAttestations: attestedEvents.length,
    totalRevocations: revokedEvents.length,
    uniqueAttesters: new Set(attestedEvents.map(e => e.args.attester)).size,
    uniqueRecipients: new Set(attestedEvents.map(e => e.args.recipient)).size,
    topAttesters: getTopAttesters(attestedEvents),
    mostAttestedUsers: getMostAttestedUsers(attestedEvents, revokedEvents),
    schemaDistribution: getSchemaDistribution(attestedEvents)
  };
  
  return analytics;
}

function getTopAttesters(events) {
  const counts = {};
  events.forEach(e => {
    counts[e.args.attester] = (counts[e.args.attester] || 0) + 1;
  });
  
  return Object.entries(counts)
    .sort(([,a], [,b]) => b - a)
    .slice(0, 10)
    .map(([attester, count]) => ({ attester, count }));
}

function getMostAttestedUsers(attestedEvents, revokedEvents) {
  const counts = {};
  
  attestedEvents.forEach(e => {
    counts[e.args.recipient] = (counts[e.args.recipient] || 0) + 1;
  });
  
  revokedEvents.forEach(e => {
    counts[e.args.recipient] = (counts[e.args.recipient] || 0) - 1;
  });
  
  return Object.entries(counts)
    .sort(([,a], [,b]) => b - a)
    .slice(0, 10)
    .map(([recipient, count]) => ({ recipient, activeAttestations: count }));
}

function getSchemaDistribution(events) {
  const counts = {};
  events.forEach(e => {
    counts[e.args.schema] = (counts[e.args.schema] || 0) + 1;
  });
  
  return Object.entries(counts)
    .map(([schema, count]) => ({ schema, count }))
    .sort((a, b) => b.count - a.count);
}

Best Practices

Event Handling

  1. Use Indexed Parameters: Filter efficiently using indexed fields
  2. Store Event Data: Cache important events off-chain
  3. Monitor Continuously: Set up real-time listeners
  4. Handle Reorgs: Account for blockchain reorganizations
  5. Rate Limiting: Don’t overwhelm with event processing

Performance

  1. Limit Block Ranges: Query smaller ranges to avoid timeouts
  2. Pagination: Implement pagination for large datasets
  3. Batch Processing: Process events in batches
  4. Caching: Cache frequently accessed event data
  5. Indexing: Use subgraphs or custom indexers