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