Real-World Examples

Common Workflows

Practical examples for building on Influence Protocol: dashboards, analytics, and integrations.

Note: All examples assume Base Mainnet. The SDK defaults to Base Sepolia, so Base Mainnet examples include custom contract config. For Base Sepolia, you can omit the config parameter.

Workflow 1: Participant Dashboard

Build a dashboard showing a participant's influence graph and attestations:

import { createRelationalClient } from '@influence-protocol/sdk';
import { JsonRpcProvider } from 'ethers';

async function buildParticipantDashboard(participantAddress: string) {
  const provider = new JsonRpcProvider('https://mainnet.base.org');
  // For Base Mainnet, pass custom contract addresses
  const client = createRelationalClient(provider, {
    visualizationAssembler: '0x...', // Base Mainnet address
    relationalResolver: '0xdaabbfc98a09f542f5f3f13694284ca96dd32934',
    illuminationAnalyzer: '0x4b2f2a03ed6744df981198fc3c231953179db699',
  });

  // Get participant graph
  const graph = await client.getParticipantGraph(participantAddress);
  
  // Get attestation summaries
  const summaries = await client.getParticipantSummaries(participantAddress, {
    offset: 0,
    limit: 50,
  });
  
  // Get influence metrics
  const metrics = await client.getInfluencerMetrics(participantAddress);
  
  // Get all creator tokens
  const creatorTokens = await client.getParticipantCreatorTokens(participantAddress);

  return {
    graph: {
      nodes: graph.nodes,
      edges: graph.edges,
    },
    attestations: summaries.items,
    totalAttestations: summaries.total,
    metrics: {
      creatorPillar: metrics.breakdown.creatorPillar,
      portfolioPillar: metrics.breakdown.portfolioPillar,
      networkPillar: metrics.breakdown.networkPillar,
      consistencyPillar: metrics.breakdown.consistencyPillar,
      totalScore: metrics.totalScore,
    },
    creatorTokens,
  };
}

Workflow 2: Creator Analytics

Analyze a creator's network and participant engagement:

async function analyzeCreator(creatorTokenAddress: string) {
  const provider = new JsonRpcProvider('https://mainnet.base.org');
  const client = createRelationalClient(provider, {
    visualizationAssembler: '0x...', // Base Mainnet address
    relationalResolver: '0xdaabbfc98a09f542f5f3f13694284ca96dd32934',
    illuminationAnalyzer: '0x4b2f2a03ed6744df981198fc3c231953179db699',
  });

  // Get creator topology
  const topology = await client.getCreatorTopology(creatorTokenAddress);
  
  // Get attestation summaries
  const summaries = await client.getCreatorTokenSummaries(creatorTokenAddress, {
    offset: 0,
    limit: 100,
  });
  
  // Get all participants
  const participants = await client.getCreatorTokenParticipants(creatorTokenAddress);

  // Calculate engagement metrics
  const uniqueParticipants = new Set(participants).size;
  const totalAttestations = summaries.total;
  const avgAttestationsPerParticipant = totalAttestations / uniqueParticipants;

  return {
    creatorNode: topology.node,
    participants: {
      total: uniqueParticipants,
      addresses: participants,
    },
    attestations: {
      total: totalAttestations,
      recent: summaries.items.slice(0, 10),
      avgPerParticipant: avgAttestationsPerParticipant,
    },
  };
}

Workflow 3: Subgraph Dashboard

Build a fast dashboard using subgraph queries:

async function buildSubgraphDashboard() {
  const endpoint = 'https://api.thegraph.com/subgraphs/name/aaronvick/radiant';
  
  const query = `
    {
      # Get active creators
      activeCreators: creatorTokens(
        where: { active: true }
        first: 100
      ) {
        id
        symbol
        name
        attestationCount
        holderCount
      }
      
      # Get recent attestations
      recentAttestations: attestations(
        first: 20
        orderBy: timestamp
        orderDirection: desc
      ) {
        id
        creatorToken {
          symbol
          name
        }
        participant
        timestamp
        illuminationCount
      }
      
      # Get top creators by attestations
      topCreators: creatorTokens(
        where: { attestationCount_gt: 0 }
        first: 10
        orderBy: attestationCount
        orderDirection: desc
      ) {
        id
        symbol
        name
        attestationCount
      }
    }
  `;
  
  const res = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query }),
  });
  
  const { data } = await res.json();
  
  return {
    activeCreators: data.activeCreators.length,
    creators: data.activeCreators,
    recentAttestations: data.recentAttestations,
    topCreators: data.topCreators,
  };
}

Workflow 4: Hybrid Query Pattern

Combine subgraph for lists, contracts for validation:

async function hybridQuery(participantAddress: string) {
  // Step 1: Use subgraph for fast list of attestation UIDs
  const subgraphQuery = `
    {
      attestations(where: { participant: "${participantAddress}" }) {
        id
      }
    }
  `;
  
  const subgraphRes = await fetch('https://api.thegraph.com/subgraphs/name/aaronvick/radiant', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query: subgraphQuery }),
  });
  
  const { data } = await subgraphRes.json();
  const uids = data.attestations.map((a: any) => a.id);
  
  // Step 2: Use contracts for authoritative record data
  const provider = new JsonRpcProvider('https://mainnet.base.org');
  const client = createRelationalClient(provider, {
    visualizationAssembler: '0x...', // Base Mainnet address
    relationalResolver: '0xdaabbfc98a09f542f5f3f13694284ca96dd32934',
    illuminationAnalyzer: '0x4b2f2a03ed6744df981198fc3c231953179db699',
  });
  
  // Batch get attestation records
  const records = await client.batchGetAttestations(uids);
  
  // Step 3: Validate and combine
  return records.map((record, i) => ({
    uid: uids[i],
    ...record,
    // Add any additional processing
  }));
}

Workflow 5: Illumination Flow

Complete flow for illuminating an attestation:

import { Wallet } from 'ethers';
import { createRelationalClient, buildIlluminateTx } from '@influence-protocol/sdk';

async function illuminateAttestation(
  attestationUid: string,
  radiantAmount: bigint,
  privateKey: string
) {
  const provider = new JsonRpcProvider('https://mainnet.base.org');
  const signer = new Wallet(privateKey, provider);
  const client = createRelationalClient(provider, {
    visualizationAssembler: '0x...', // Base Mainnet address
    relationalResolver: '0xdaabbfc98a09f542f5f3f13694284ca96dd32934',
    illuminationAnalyzer: '0x4b2f2a03ed6744df981198fc3c231953179db699',
  });
  
  // Step 1: Validate attestation exists
  const record = await client.getAttestationRecord(attestationUid);
  if (!record) {
    throw new Error('Attestation not found');
  }
  
  // Step 2: Check if already illuminated (optional)
  // You might want to check illumination stats here
  
  // Step 3: Build transaction
  const tx = buildIlluminateTx({
    registry: '0x2aaca41ef9bb156ce3c302e696cff5e7e2b974bf',
    attestationUid,
    radiantAmount,
  });
  
  // Step 4: Send transaction
  const receipt = await signer.sendTransaction(tx);
  await receipt.wait();
  
  return {
    txHash: receipt.hash,
    attestationUid,
    amount: radiantAmount,
  };
}

Workflow 6: Creator Network Visualization

Build a network graph showing connections between creators:

async function buildCreatorNetwork(creatorTokenAddresses: string[]) {
  const provider = new JsonRpcProvider('https://mainnet.base.org');
  const client = createRelationalClient(provider, {
    visualizationAssembler: '0x...', // Base Mainnet address
    relationalResolver: '0xdaabbfc98a09f542f5f3f13694284ca96dd32934',
    illuminationAnalyzer: '0x4b2f2a03ed6744df981198fc3c231953179db699',
  });
  
  // Get network edges
  const edges = await client.getCreatorNetwork(creatorTokenAddresses);
  
  // Get topology for each creator
  const topologies = await Promise.all(
    creatorTokenAddresses.map(addr => 
      client.getCreatorTopology(addr)
    )
  );
  
  // Build graph structure
  const nodes = topologies.map(t => t.node);
  const networkEdges = edges;
  
  // Calculate shared participants
  const sharedParticipants = new Map();
  for (const edge of edges) {
    const key = `${edge.fromToken}-${edge.toToken}`;
    sharedParticipants.set(key, edge.sharedParticipants);
  }
  
  return {
    nodes,
    edges: networkEdges,
    sharedParticipants: Object.fromEntries(sharedParticipants),
  };
}

Workflow 7: Influence Leaderboard

Build a leaderboard of top influencers:

async function buildInfluenceLeaderboard(limit: number = 100) {
  const provider = new JsonRpcProvider('https://mainnet.base.org');
  const client = createRelationalClient(provider, {
    visualizationAssembler: '0x...', // Base Mainnet address
    relationalResolver: '0xdaabbfc98a09f542f5f3f13694284ca96dd32934',
    illuminationAnalyzer: '0x4b2f2a03ed6744df981198fc3c231953179db699',
  });
  
  // Step 1: Get all participants from subgraph (faster)
  const subgraphQuery = `
    {
      attestations(first: 1000) {
        participant
      }
    }
  `;
  
  const res = await fetch('https://api.thegraph.com/subgraphs/name/aaronvick/radiant', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query: subgraphQuery }),
  });
  
  const { data } = await res.json();
  const uniqueParticipants = [...new Set(data.attestations.map((a: any) => a.participant))];
  
  // Step 2: Get metrics for each participant
  const metricsPromises = uniqueParticipants.slice(0, limit).map(addr =>
    client.getInfluencerMetrics(addr).catch(() => null)
  );
  
  const metricsResults = await Promise.all(metricsPromises);
  
  // Step 3: Sort by total score
  const leaderboard = metricsResults
    .filter(m => m !== null)
    .map((metrics, i) => ({
      address: uniqueParticipants[i],
      totalScore: metrics!.totalScore,
      breakdown: metrics!.breakdown,
      rank: metrics!.rank,
    }))
    .sort((a, b) => Number(b.totalScore - a.totalScore))
    .slice(0, limit);
  
  return leaderboard;
}

Workflow 8: Real-time Attestation Monitor

Monitor new attestations in real-time:

import { Contract, JsonRpcProvider } from 'ethers';

async function monitorAttestations(creatorTokenAddress: string, callback: (uid: string) => void) {
  const provider = new JsonRpcProvider('https://mainnet.base.org');
  const client = createRelationalClient(provider, {
    visualizationAssembler: '0x...', // Base Mainnet address
    relationalResolver: '0xdaabbfc98a09f542f5f3f13694284ca96dd32934',
    illuminationAnalyzer: '0x4b2f2a03ed6744df981198fc3c231953179db699',
  });
  
  // Get current attestations
  let knownUids = new Set(await client.getCreatorTokenAttestations(creatorTokenAddress));
  
  // Poll for new attestations
  setInterval(async () => {
    const currentUids = await client.getCreatorTokenAttestations(creatorTokenAddress);
    const newUids = currentUids.filter(uid => !knownUids.has(uid));
    
    for (const uid of newUids) {
      knownUids.add(uid);
      callback(uid);
    }
  }, 5000); // Poll every 5 seconds
}

// Usage
monitorAttestations('0x...', async (uid) => {
  const record = await client.getAttestationRecord(uid);
  console.log('New attestation:', record);
});

Best Practices

  • Use subgraph for lists and aggregations: Faster and more efficient for large datasets.
  • Use contracts for validation: Always validate critical data on-chain before transactions.
  • Batch operations: Use batchGetAttestations instead of multiple individual calls.
  • Cache results: Subgraph data can be cached for a few seconds; contract data should be fresh.
  • Handle errors gracefully: Subgraph may lag; fall back to contracts when needed.
  • Normalize addresses: Always checksum addresses before comparing or storing.
  • Paginate large queries: Use offset/limit for large result sets to avoid timeouts.

Agent Notes

  • These workflows are production-ready patterns used in the Influence Protocol ecosystem.
  • Always validate user inputs (addresses, amounts) before making contract calls.
  • Consider rate limiting when polling contracts or subgraph in production.
  • Use TypeScript for type safety when working with SDK responses.
  • Monitor gas costs when batching operations; some may be more efficient as separate calls.