Skip to main content

Complete Workflow

These examples demonstrate end-to-end workflows for both Stellar and Solana. Each example includes error handling and best practices.

Platform Examples

  • Stellar
  • Solana
import { AttestSDK, StellarConfig } from '@attestprotocol/sdk';

async function stellarWorkflow() {
const config: StellarConfig = {
secretKeyOrCustomSigner: process.env.STELLAR_SECRET_KEY!,
publicKey: process.env.STELLAR_PUBLIC_KEY!,
url: 'https://soroban-testnet.stellar.org'
};

const sdk = await AttestSDK.initializeStellar(config);

// Register as authority
const authResult = await sdk.registerAuthority();
if (authResult.error) return authResult.error;

// Create schema
const schemaResult = await sdk.createSchema({
schemaName: 'kyc-verification-v1',
schemaContent: 'verified:bool,level:string,score:uint8,timestamp:uint64',
revocable: true
});
if (schemaResult.error) return schemaResult.error;

const schemaUID = schemaResult.data!.schemaUID;

// Wait for indexing
await new Promise(resolve => setTimeout(resolve, 10000));

// Create attestation
const attestResult = await sdk.attest({
schemaUID: schemaUID,
subject: 'GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
value: 'verified:true,level:enhanced,score:95,timestamp:1704067200',
reference: 'kyc-check-2024-001'
});
if (attestResult.error) return attestResult.error;

// Fetch attestation
const fetchResult = await sdk.fetchAttestation({
schemaUID: schemaUID,
subject: 'GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
reference: 'kyc-check-2024-001'
});

  return fetchResult.data;
}
Both examples follow the same workflow: initialize SDK → register authority → create schema → create attestation → fetch attestation.

Express.js API Integration

Always validate input data and implement proper authentication before production deployment.
import express from 'express';
import { AttestSDK, StellarConfig, SolanaConfig } from '@attestprotocol/sdk';

const app = express();
app.use(express.json());

let stellarSDK: any;
let solanaSDK: any;

async function initializeSDKs() {
  stellarSDK = await AttestSDK.initializeStellar({
    secretKeyOrCustomSigner: process.env.STELLAR_SECRET_KEY!,
    publicKey: process.env.STELLAR_PUBLIC_KEY!
  });

  solanaSDK = await AttestSDK.initializeSolana({
    walletOrSecretKey: JSON.parse(process.env.SOLANA_SECRET_KEY!)
  });
}

app.post('/api/attest', async (req, res) => {
  const { chain, schemaUID, subject, data, reference } = req.body;

  try {
    let result;
    if (chain === 'stellar') {
      result = await stellarSDK.attest({
        schemaUID,
        subject,
        value: data,
        reference
      });
    } else if (chain === 'solana') {
      const balanceResult = await solanaSDK.getWalletBalance();
      result = await solanaSDK.attest({
        schemaData: new anchor.web3.PublicKey(schemaUID),
        data,
        accounts: {
          recipient: new anchor.web3.PublicKey(subject),
          levyReceipent: balanceResult.data!.address,
          mintAccount: new anchor.web3.PublicKey('So11111111111111111111111111111111111111112')
        }
      });
    } else {
      return res.status(400).json({ error: 'Unsupported chain' });
    }

    if (result.error) {
      return res.status(500).json({ error: result.error });
    }

    res.json({ 
      success: true, 
      attestationId: result.data,
      chain 
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.get('/api/verify/:chain/:schema/:subject', async (req, res) => {
  const { chain, schema, subject } = req.params;

  try {
    let result;
    if (chain === 'stellar') {
      result = await stellarSDK.fetchAttestation({
        schemaUID: schema,
        subject: subject
      });
    } else if (chain === 'solana') {
      result = await solanaSDK.fetchAttestation(subject);
    } else {
      return res.status(400).json({ error: 'Unsupported chain' });
    }

    if (result.error) {
      return res.status(500).json({ error: result.error });
    }

    res.json({
      verified: !!result.data && !result.data.revoked,
      attestation: result.data,
      chain
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

initializeSDKs().then(() => {
  app.listen(3000);
});

Batch Operations

Batch operations enable efficient processing of multiple attestations. Use Promise.all() for parallel execution.

Multiple Attestations

async function batchAttestations() {
  const sdk = await AttestSDK.initializeSolana({
    walletOrSecretKey: JSON.parse(process.env.SOLANA_SECRET_KEY!)
  });

  await sdk.registerAuthority();

  const schemaResult = await sdk.createSchema({
    schemaName: 'user-verification-batch',
    schemaContent: 'verified:bool,tier:string,score:u8',
    revocable: true
  });

  if (schemaResult.error) throw schemaResult.error;

  const schemaPDA = schemaResult.data!;
  const walletBalance = await sdk.getWalletBalance();
  
  const users = [
    { address: '11111111111111111111111111111111', data: 'verified:true,tier:gold,score:95' },
    { address: '22222222222222222222222222222222', data: 'verified:true,tier:silver,score:80' },
    { address: '33333333333333333333333333333333', data: 'verified:true,tier:bronze,score:65' }
  ];

  const attestationPromises = users.map(user => sdk.attest({
    schemaData: schemaPDA,
    data: user.data,
    accounts: {
      recipient: new anchor.web3.PublicKey(user.address),
      levyReceipent: walletBalance.data!.address,
      mintAccount: new anchor.web3.PublicKey('So11111111111111111111111111111111111111112')
    }
  }));

  const results = await Promise.all(attestationPromises);
  
  return results.filter(result => !result.error).map(result => result.data);
}
When performing batch operations, consider rate limits and transaction costs. Implement retry logic for failed attestations.

React Component

Never expose private keys in client-side code. Use server-side APIs or wallet integrations for production.
import React, { useState, useEffect } from 'react';
import { AttestSDK } from '@attestprotocol/sdk';

interface AttestationFormProps {
  chain: 'stellar' | 'solana';
}

// Custom hook for AttestSDK
export function useAttestSDK(chain: 'stellar' | 'solana') {
  const [sdk, setSdk] = useState<any>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    async function init() {
      try {
        let instance;
        if (chain === 'stellar') {
          instance = await AttestSDK.initializeStellar(stellarConfig);
        } else {
          instance = await AttestSDK.initializeSolana(solanaConfig);
        }
        setSdk(instance);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    init();
  }, [chain]);

  return { sdk, loading, error };
}

Error Handling

Proper error handling is crucial for production applications. The examples below show comprehensive error management strategies.

Comprehensive Error Handler

  • Error Handler Class
  • Error Constants
class AttestSDKErrorHandler {
handleStellarError(error: any): string {
if (error.status === 404) {
  return "Account not found. Fund your Stellar account.";
}

if (error.message?.includes('insufficient balance')) {
  return "Insufficient XLM balance.";
}

if (error.message?.includes('Invalid schema UID')) {
  return "Schema UID must be 64-character hex string.";
}

return `Stellar error: ${error.message || 'Unknown error'}`;
}

handleSolanaError(error: any): string {
if (error.code === 6010) {
  return "Schema with this name already exists.";
}

if (error.code === 6002) {
  return "Attestation is already revoked.";
}

if (error.message?.includes('Insufficient funds')) {
  return "Insufficient SOL balance.";
}

return `Solana error: ${error.message || 'Unknown error'}`;
}
}

async function robustOperation() {
const errorHandler = new AttestSDKErrorHandler();
const stellarSDK = await AttestSDK.initializeStellar(config);

const result = await stellarSDK.attest({
schemaUID: 'abc123...',
subject: 'GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
value: 'verified:true',
reference: 'test-001'
});

if (result.error) {
const errorMsg = errorHandler.handleStellarError(result.error);
throw new Error(errorMsg);
}

  return result.data;
}

Environment Configuration

Use environment variables to manage configuration across different environments (development, staging, production).

Configuration Utility

interface EnvironmentConfig {
  stellar: StellarConfig;
  solana: SolanaConfig;
}

function getConfig(): EnvironmentConfig {
  const requiredEnvVars = [
    'STELLAR_SECRET_KEY',
    'STELLAR_PUBLIC_KEY', 
    'SOLANA_SECRET_KEY'
  ];

  for (const envVar of requiredEnvVars) {
    if (!process.env[envVar]) {
      throw new Error(`Missing required environment variable: ${envVar}`);
    }
  }

  return {
    stellar: {
      secretKeyOrCustomSigner: process.env.STELLAR_SECRET_KEY!,
      publicKey: process.env.STELLAR_PUBLIC_KEY!,
      url: process.env.STELLAR_RPC_URL || 'https://soroban-testnet.stellar.org'
    },
    solana: {
      walletOrSecretKey: JSON.parse(process.env.SOLANA_SECRET_KEY!),
      url: process.env.SOLANA_RPC_URL || 'https://api.devnet.solana.com',
      programId: process.env.SOLANA_PROGRAM_ID
    }
  };
}

// Usage
const config = getConfig();
const stellarSDK = await AttestSDK.initializeStellar(config.stellar);
const solanaSDK = await AttestSDK.initializeSolana(config.solana);

Testing

Always use testnet/devnet for testing. Never run tests against mainnet to avoid unnecessary costs.

Jest Integration Tests

  • Jest Tests
  • Test Utilities
describe('AttestSDK Integration Tests', () => {
let stellarSDK: any;
let testSchemaUID: string;

beforeAll(async () => {
stellarSDK = await AttestSDK.initializeStellar({
  secretKeyOrCustomSigner: process.env.TEST_STELLAR_SECRET_KEY!,
  publicKey: process.env.TEST_STELLAR_PUBLIC_KEY!
});

const authResult = await stellarSDK.registerAuthority();
expect(authResult.error).toBeUndefined();
});

test('should create schema successfully', async () => {
const result = await stellarSDK.createSchema({
  schemaName: 'test-schema-jest',
  schemaContent: 'test:bool,value:uint8',
  revocable: true
});

expect(result.error).toBeUndefined();
expect(result.data).toBeDefined();
expect(result.data.schemaUID).toMatch(/^[a-f0-9]{64}$/);

testSchemaUID = result.data.schemaUID;
});

test('should create attestation successfully', async () => {
const result = await stellarSDK.attest({
  schemaUID: testSchemaUID,
  subject: 'GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  value: 'test:true,value:42',
  reference: 'jest-test-001'
});

expect(result.error).toBeUndefined();
expect(result.data).toBeDefined();
expect(typeof result.data).toBe('string');
});

test('should handle invalid schema UID', async () => {
const result = await stellarSDK.fetchSchema('invalid-uid');

expect(result.error).toBeDefined();
expect(result.data).toBeUndefined();
});
});

Production Patterns

These patterns help build robust, scalable applications with AttestProtocol.

Retry Logic

async function withRetry<T>(
  operation: () => Promise<AttestSDKResponse<T>>,
  maxRetries: number = 3
): Promise<AttestSDKResponse<T>> {
  let lastError: any;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const result = await operation();
    
    if (!result.error) {
      return result;
    }
    
    lastError = result.error;
    
    const errorStr = result.error.toString().toLowerCase();
    if (errorStr.includes('unauthorized') || 
        errorStr.includes('invalid') ||
        errorStr.includes('already exists')) {
      break;
    }
    
    if (attempt < maxRetries) {
      const delay = 1000 * Math.pow(2, attempt - 1);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  return { error: lastError };
}

// Usage
const result = await withRetry(
  () => sdk.attest(attestConfig),
  3 // max retries
);

Connection Pool

Connection pooling improves performance for high-throughput applications by reusing SDK instances.
class SDKConnectionPool {
  private stellarInstances: StellarAttestSDK[] = [];
  private solanaInstances: SolanaAttestSDK[] = [];
  private currentStellar = 0;
  private currentSolana = 0;

  async initialize(poolSize: number = 3) {
    const stellarPromises = Array(poolSize).fill(null).map(() =>
      AttestSDK.initializeStellar({
        secretKeyOrCustomSigner: process.env.STELLAR_SECRET_KEY!,
        publicKey: process.env.STELLAR_PUBLIC_KEY!
      })
    );

    const solanaPromises = Array(poolSize).fill(null).map(() =>
      AttestSDK.initializeSolana({
        walletOrSecretKey: JSON.parse(process.env.SOLANA_SECRET_KEY!)
      })
    );

    this.stellarInstances = await Promise.all(stellarPromises);
    this.solanaInstances = await Promise.all(solanaPromises);
  }

  getStellarSDK(): StellarAttestSDK {
    const sdk = this.stellarInstances[this.currentStellar];
    this.currentStellar = (this.currentStellar + 1) % this.stellarInstances.length;
    return sdk;
  }

  getSolanaSDK(): SolanaAttestSDK {
    const sdk = this.solanaInstances[this.currentSolana];  
    this.currentSolana = (this.currentSolana + 1) % this.solanaInstances.length;
    return sdk;
  }
}

// Usage
const pool = new SDKConnectionPool();
await pool.initialize(5); // 5 instances per chain

// Use in request handler
app.get('/api/attest', async (req, res) => {
  const sdk = req.query.chain === 'stellar' 
    ? pool.getStellarSDK() 
    : pool.getSolanaSDK();
  
  // Process request with SDK instance
});

Additional Resources