Skip to main content

Installation

npm install @crossmint/client-sdk-verifiable-credentials

Overview

The Verifiable Credentials SDK enables applications to work with verifiable credentials (VCs) stored as NFTs on multiple blockchains. It supports:
  • Fetching credentials from user wallets
  • Verifying credential authenticity and validity
  • Decrypting encrypted credentials
  • Presenting credentials to verifiers

Quick Start

import { 
  getCredentialNfts,
  verifyCredential,
  type VerifiableCredential 
} from '@crossmint/client-sdk-verifiable-credentials';

// Fetch all credentials for a wallet
const collections = await getCredentialNfts(
  'polygon',
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
);

// Get first credential
const credential = collections[0].credentials[0];

// Verify the credential
const isValid = await verifyCredential(credential);

console.log('Credential valid:', isValid);

Core Concepts

Verifiable Credential Structure

Credentials follow the W3C Verifiable Credentials standard:
interface VerifiableCredential {
  id: string;
  credentialSubject: any; // Claims about the subject
  issuer: { id: string }; // Issuer DID
  type: string[]; // Credential types
  validFrom: string; // ISO 8601 date
  validUntil?: string; // Optional expiration
  name?: string;
  description?: string;
  nft: Nft; // Associated NFT metadata
  '@context': string[];
  proof?: { // Cryptographic proof
    proofValue: string;
    [key: string]: any;
  };
}
See type definition at /home/daytona/workspace/source/packages/client/verifiable-credentials/src/verifiableCredentialsSDK/types/verifiableCredential.ts

Encrypted Credentials

Some credentials are encrypted on-chain:
interface EncryptedVerifiableCredential {
  id: string;
  payload: string; // Encrypted credential data
}

type VerifiableCredentialType = 
  | VerifiableCredential 
  | EncryptedVerifiableCredential;

Fetching Credentials

Get Credential NFTs

Fetch all verifiable credentials for a wallet:
import { getCredentialNfts, type VCChain } from '@crossmint/client-sdk-verifiable-credentials';

const collections = await getCredentialNfts(
  'polygon' as VCChain,
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  {
    // Optional filters
    credentialTypes: ['VerifiableCredential', 'EmploymentCredential'],
    issuerAddress: '0x123...', // Filter by issuer
  }
);

collections.forEach(collection => {
  console.log('Collection:', collection.name);
  console.log('Credentials:', collection.credentials.length);
  
  collection.credentials.forEach(cred => {
    console.log('  -', cred.name, '(Type:', cred.type, ')');
  });
});

Parameters

chain
VCChain
required
Blockchain to query. Supported chains:
  • 'polygon'
  • 'ethereum'
  • 'arbitrum'
  • 'optimism'
  • 'base'
  • 'zora'
wallet
string
required
Wallet address to fetch credentials for
filters
CredentialFilter
Optional filters:
{
  credentialTypes?: string[]; // Filter by credential type
  issuerAddress?: string; // Filter by issuer address
}

Return Type

interface CredentialsCollection {
  name: string; // Collection name
  credentials: VerifiableCredentialType[];
  contractAddress: string;
  chain: VCChain;
}
See implementation at /home/daytona/workspace/source/packages/client/verifiable-credentials/src/presentation/getCredentialNfts.ts

Get Single Credential

Fetch a specific credential by NFT locator:
import { 
  getCredentialNFTFromLocator,
  CredentialService 
} from '@crossmint/client-sdk-verifiable-credentials';

// Fetch credential NFT metadata
const nft = await getCredentialNFTFromLocator(
  'polygon:0x123.../tokenId/456',
  { /* chain RPC config */ }
);

// Fetch full credential data
const credentialService = new CredentialService();
const credential = await credentialService.getCredential(
  nft,
  'polygon'
);

Verifying Credentials

Verify Credential

Verify a credential’s authenticity and validity:
import { verifyCredential } from '@crossmint/client-sdk-verifiable-credentials';

const isValid = await verifyCredential(credential);

if (isValid) {
  console.log('Credential is valid and verified');
} else {
  console.log('Credential verification failed');
}
Verification checks:
  • Cryptographic proof signature
  • Expiration date (if present)
  • Issuer signature
  • Credential format compliance
Encrypted credentials must be decrypted before verification.

Decrypting Credentials

Using Lit Protocol

Decrypt credentials encrypted with Lit Protocol:
import { Lit } from '@crossmint/client-sdk-verifiable-credentials';
import type { EncryptedVerifiableCredential } from '@crossmint/client-sdk-verifiable-credentials';

// Initialize Lit client
const lit = new Lit();
await lit.connect();

// Decrypt credential
const decrypted = await lit.decryptVerifiableCredential(
  encryptedCredential as EncryptedVerifiableCredential,
  authSig, // Wallet signature for authentication
  chain
);

console.log('Decrypted credential:', decrypted);

Using Wallet Decryption

Decrypt using a wallet’s native encryption:
import { 
  CrossmintDecrypt,
  CrossmintMetamaskDecrypt 
} from '@crossmint/client-sdk-verifiable-credentials';

// Generic wallet decryption
const decryptor = new CrossmintDecrypt(walletProvider);
const decrypted = await decryptor.decrypt(encryptedCredential);

// MetaMask-specific decryption
const metamaskDecryptor = new CrossmintMetamaskDecrypt(ethereum);
const decrypted = await metamaskDecryptor.decrypt(encryptedCredential);

Credential Presentation

Contract Metadata Service

Fetch metadata about credential contracts:
import { ContractMetadataService } from '@crossmint/client-sdk-verifiable-credentials';

const metadataService = new ContractMetadataService();

const metadata = await metadataService.getContractMetadata(
  'polygon',
  '0x123...' // Contract address
);

console.log('Contract name:', metadata.name);
console.log('Contract description:', metadata.description);
console.log('Contract image:', metadata.image);

Type Utilities

Type Guards

Check credential types:
import {
  isVerifiableCredential,
  isEncryptedVerifiableCredential,
  isCredentialType,
  isVcChain,
} from '@crossmint/client-sdk-verifiable-credentials';

if (isVerifiableCredential(credential)) {
  // TypeScript knows this is VerifiableCredential
  console.log('Issuer:', credential.issuer.id);
}

if (isEncryptedVerifiableCredential(credential)) {
  // TypeScript knows this is EncryptedVerifiableCredential
  console.log('Encrypted payload:', credential.payload);
}

if (isCredentialType(credential, 'EmploymentCredential')) {
  console.log('This is an employment credential');
}

if (isVcChain('polygon')) {
  // TypeScript knows this is a valid VCChain
}

Advanced Features

Custom Retrieval Procedures

Implement custom credential retrieval:
import { 
  ipfsRetrievalProcedure,
  crossmintRetrievalProcedure 
} from '@crossmint/client-sdk-verifiable-credentials';

// Retrieve from IPFS
const credential = await ipfsRetrievalProcedure(
  'ipfs://QmXxx...',
  'polygon'
);

// Retrieve from Crossmint API
const credential2 = await crossmintRetrievalProcedure(
  nft,
  'ethereum'
);

Wallet Authentication Service

Authenticate wallet signatures for credential operations:
import { WalletAuthService } from '@crossmint/client-sdk-verifiable-credentials';

const authService = new WalletAuthService();

const authSig = await authService.getAuthSignature(
  wallet,
  'polygon'
);

// Use authSig for Lit Protocol or other operations

Crossmint API Client

Direct API access for advanced use cases:
import { crossmintAPI } from '@crossmint/client-sdk-verifiable-credentials';

const client = crossmintAPI({
  apiKey: 'YOUR_API_KEY',
  baseUrl: 'https://www.crossmint.com/api',
});

// Make custom API calls
const response = await client.get('/credentials/...');

Type Exports

All exported types:
import type {
  // Core credential types
  VerifiableCredential,
  EncryptedVerifiableCredential,
  VerifiableCredentialType,
  
  // NFT types
  VCNFT,
  
  // Chain types
  VCChain,
  ChainRPCConfig,
  
  // Collection types
  Collection,
  CredentialsCollection,
  CredentialMetadata,
  
  // Filter types
  CredentialFilter,
  
  // Encryption types
  VerifiableCredentialEncryption,
  VerifiableCredentialEncryptionType,
} from '@crossmint/client-sdk-verifiable-credentials';

Complete Example

import {
  getCredentialNfts,
  verifyCredential,
  isVerifiableCredential,
  isEncryptedVerifiableCredential,
  Lit,
  type VCChain,
  type VerifiableCredential,
} from '@crossmint/client-sdk-verifiable-credentials';

async function fetchAndVerifyCredentials(
  chain: VCChain,
  walletAddress: string
) {
  // 1. Fetch all credentials
  const collections = await getCredentialNfts(chain, walletAddress, {
    credentialTypes: ['VerifiableCredential', 'EmploymentCredential'],
  });

  console.log(`Found ${collections.length} collections`);

  // 2. Process each credential
  for (const collection of collections) {
    console.log(`\nCollection: ${collection.name}`);

    for (const credential of collection.credentials) {
      // 3. Handle encrypted credentials
      if (isEncryptedVerifiableCredential(credential)) {
        console.log('  - Encrypted credential found');
        
        // Decrypt with Lit Protocol
        const lit = new Lit();
        await lit.connect();
        
        const decrypted = await lit.decryptVerifiableCredential(
          credential,
          authSig,
          chain
        );
        
        credential = decrypted;
      }

      // 4. Verify credential
      if (isVerifiableCredential(credential)) {
        const isValid = await verifyCredential(credential);
        
        console.log(`  - ${credential.name}:`, isValid ? '✓ Valid' : '✗ Invalid');
        console.log(`    Type: ${credential.type.join(', ')}`);
        console.log(`    Issuer: ${credential.issuer.id}`);
        console.log(`    Valid from: ${credential.validFrom}`);
        
        if (credential.validUntil) {
          const expired = new Date(credential.validUntil) < new Date();
          console.log(`    Valid until: ${credential.validUntil} ${expired ? '(EXPIRED)' : ''}`);
        }
      }
    }
  }
}

// Usage
await fetchAndVerifyCredentials(
  'polygon',
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
);

Best Practices

Never trust credential data without verification:
const isValid = await verifyCredential(credential);
if (!isValid) {
  throw new Error('Invalid credential');
}
// Now safe to use credential.credentialSubject
Verify credentials haven’t expired:
if (credential.validUntil) {
  const expired = new Date(credential.validUntil) < new Date();
  if (expired) {
    console.warn('Credential has expired');
  }
}
Always check if a credential is encrypted before accessing data:
if (isEncryptedVerifiableCredential(credential)) {
  credential = await decrypt(credential);
}

if (isVerifiableCredential(credential)) {
  // Now safe to access credentialSubject
  console.log(credential.credentialSubject);
}
Use filters to reduce data transfer and processing:
const collections = await getCredentialNfts(
  'polygon',
  walletAddress,
  {
    credentialTypes: ['EmploymentCredential'],
    issuerAddress: trustedIssuerAddress,
  }
);

Error Handling

import { 
  getCredentialNfts,
  verifyCredential 
} from '@crossmint/client-sdk-verifiable-credentials';

try {
  const collections = await getCredentialNfts('polygon', walletAddress);
  
  for (const collection of collections) {
    for (const credential of collection.credentials) {
      try {
        const isValid = await verifyCredential(credential);
        if (!isValid) {
          console.warn('Credential verification failed:', credential.id);
        }
      } catch (error) {
        console.error('Verification error:', error);
        // Continue processing other credentials
      }
    }
  }
} catch (error) {
  console.error('Failed to fetch credentials:', error);
  // Handle network or API errors
}

Supported Chains

The SDK supports verifiable credentials on:
  • Polygon (polygon)
  • Ethereum (ethereum)
  • Arbitrum (arbitrum)
  • Optimism (optimism)
  • Base (base)
  • Zora (zora)

Next Steps

Issue Credentials

Learn how to issue credentials

React Integration

Use with React components

API Reference

Explore the complete API

W3C VC Standard

Learn about VC standards

Build docs developers (and LLMs) love