Skip to main content
Create verifiable proof of ownership for digital and physical assets using AttestProtocol. This guide covers ownership verification patterns for NFT marketplaces, real-world asset registries, and collateralization systems.

Use Case Overview

Asset ownership attestations create immutable, transferable proof of ownership that enables secure lending, trading, and verification across platforms. Transform ownership claims into cryptographically verifiable digital assets. Ideal For:
  • NFT marketplaces and platforms
  • Real-world asset tokenization
  • Collateral verification for lending
  • Insurance claim validation
  • Supply chain provenance
  • Luxury goods authentication
  • Digital asset custody services

Implementation

Schema Configuration

Define asset ownership attestation schemas:
// Basic asset ownership
const assetOwnershipSchema = 'asset-ownership-v1';
// Definition: assetId:string,assetType:string,owner:address,verified:bool,chainId:uint32,contractAddress:address,timestamp:uint64

// NFT ownership verification
const nftOwnershipSchema = 'nft-ownership-v1';
// Definition: tokenId:string,contractAddress:address,owner:address,metadata:string,verified:bool,chainId:uint32,marketplace:string,timestamp:uint64

// Physical asset ownership
const physicalAssetSchema = 'physical-asset-ownership-v1';
// Definition: assetId:string,assetType:string,owner:address,serialNumber:string,manufacturer:string,condition:string,location:string,verified:bool,timestamp:uint64

// Custody verification
const custodySchema = 'asset-custody-v1';
// Definition: assetId:string,custodian:address,depositor:address,custodyType:string,securityLevel:uint8,insured:bool,depositDate:uint64,timestamp:uint64

Asset Verification Oracle

Implement automated asset ownership verification:
import { StellarAttestSDK, SolanaAttestSDK } from '@attestprotocol/sdk';
import { ethers } from 'ethers';

class AssetOwnershipOracle {
  private sdk: StellarAttestSDK | SolanaAttestSDK;
  private ethereumProvider: ethers.providers.JsonRpcProvider;
  private verificationSources: Map<string, AssetVerifier>;
  
  constructor(
    sdk: StellarAttestSDK | SolanaAttestSDK,
    ethereumRpcUrl: string
  ) {
    this.sdk = sdk;
    this.ethereumProvider = new ethers.providers.JsonRpcProvider(ethereumRpcUrl);
    this.initializeVerificationSources();
  }
Main Verification Flow
  async verifyAndAttestAssetOwnership(
    ownerAddress: string,
    assetDetails: AssetDetails,
    proofData: OwnershipProof
  ): Promise<OwnershipVerificationResult> {
    const sessionId = `asset-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    
    try {
      // Step 1: Verify asset ownership
      const verificationResult = await this.performOwnershipVerification(assetDetails, proofData);
      
      // Step 2: Gather asset metadata
      const assetMetadata = await this.gatherAssetMetadata(assetDetails);
      
      // Step 3: Create and store attestation
      const attestationUID = await this.processVerificationResult(
        ownerAddress, assetDetails, assetMetadata, verificationResult, sessionId
      );
      
      return {
        success: true,
        sessionId,
        attestationUID,
        assetMetadata,
        verificationMethod: verificationResult.method,
        confidence: verificationResult.confidence
      };
      
    } catch (error) {
      await this.logVerificationError(sessionId, assetDetails, error);
      throw error;
    }
  }
Ownership Verification
  private async performOwnershipVerification(
    assetDetails: AssetDetails,
    proofData: OwnershipProof
  ): Promise<AssetVerificationResult> {
    const verificationResult = await this.verifyAssetOwnership(assetDetails, proofData);
    
    if (!verificationResult.verified) {
      throw new Error(`Asset ownership verification failed: ${verificationResult.reason}`);
    }
    
    return verificationResult;
  }
Process Verification Result
  private async processVerificationResult(
    ownerAddress: string,
    assetDetails: AssetDetails,
    assetMetadata: AssetMetadata,
    verificationResult: AssetVerificationResult,
    sessionId: string
  ): Promise<string> {
    // Create ownership attestation
    const attestationUID = await this.createOwnershipAttestation(
      ownerAddress,
      assetDetails,
      assetMetadata,
      verificationResult,
      sessionId
    );
    
    // Store verification records
    await this.storeVerificationRecord({
      sessionId,
      assetDetails,
      verificationResult,
      attestationUID,
      timestamp: Date.now()
    });
    
    return attestationUID;
  }
}

**Asset Verification**

```typescript
  private async verifyAssetOwnership(
    assetDetails: AssetDetails,
    proof: OwnershipProof
  ): Promise<AssetVerificationResult> {
    const verifier = this.verificationSources.get(assetDetails.assetType);
    
    if (!verifier) {
      throw new Error(`No verifier available for asset type: ${assetDetails.assetType}`);
    }
    
    return await verifier.verify(assetDetails, proof);
  }
Attestation Creation
  private async createOwnershipAttestation(
    ownerAddress: string,
    assetDetails: AssetDetails,
    metadata: AssetMetadata,
    verification: AssetVerificationResult,
    sessionId: string
  ): Promise<string> {
    // Determine schema and build attestation value based on asset type
    const { schemaUID, attestationValue } = this.buildAttestationData(
      ownerAddress, assetDetails, metadata
    );
    
    const attestation = await this.sdk.attest({
      schemaUID,
      subject: ownerAddress,
      value: attestationValue,
      reference: sessionId
    });
    
    if (attestation.error) {
      throw new Error(`Ownership attestation failed: ${attestation.error}`);
    }
    
    return attestation.data;
  }
Build Attestation Data
  private buildAttestationData(
    ownerAddress: string,
    assetDetails: AssetDetails,
    metadata: AssetMetadata
  ): { schemaUID: string; attestationValue: string } {
    const timestamp = Math.floor(Date.now() / 1000);
    
    switch (assetDetails.assetType) {
      case 'nft':
        return {
          schemaUID: 'nft-ownership-v1',
          attestationValue: [
            `tokenId:${assetDetails.tokenId}`,
            `contractAddress:${assetDetails.contractAddress}`,
            `owner:${ownerAddress}`,
            `metadata:${metadata.uri || 'none'}`,
            `verified:true`,
            `chainId:${assetDetails.chainId}`,
            `marketplace:${metadata.marketplace || 'unknown'}`,
            `timestamp:${timestamp}`
          ].join(',')
        };
        
      case 'physical':
        return {
          schemaUID: 'physical-asset-ownership-v1',
          attestationValue: [
            `assetId:${assetDetails.assetId}`,
            `assetType:${assetDetails.subType || 'unknown'}`,
            `owner:${ownerAddress}`,
            `serialNumber:${metadata.serialNumber || 'unknown'}`,
            `manufacturer:${metadata.manufacturer || 'unknown'}`,
            `condition:${metadata.condition || 'unknown'}`,
            `location:${metadata.location || 'unknown'}`,
            `verified:true`,
            `timestamp:${timestamp}`
          ].join(',')
        };
        
      default:
        return {
          schemaUID: 'asset-ownership-v1',
          attestationValue: [
            `assetId:${assetDetails.assetId}`,
            `assetType:${assetDetails.assetType}`,
            `owner:${ownerAddress}`,
            `verified:true`,
            `chainId:${assetDetails.chainId || 0}`,
            `contractAddress:${assetDetails.contractAddress || 'none'}`,
            `timestamp:${timestamp}`
          ].join(',')
        };
    }
  }
}

**Verification Sources Setup**

```typescript
  private initializeVerificationSources(): void {
    this.verificationSources = new Map([
      ['nft', new NFTVerifier(this.ethereumProvider)],
      ['erc20', new ERC20Verifier(this.ethereumProvider)],
      ['physical', new PhysicalAssetVerifier()],
      ['custody', new CustodyVerifier()],
      ['real_estate', new RealEstateVerifier()],
      ['vehicle', new VehicleVerifier()]
    ]);
  }

  private async gatherAssetMetadata(assetDetails: AssetDetails): Promise<AssetMetadata> {
    const verifier = this.verificationSources.get(assetDetails.assetType);
    return verifier ? await verifier.getMetadata(assetDetails) : {};
  }
}
NFT Verifier Implementation
class NFTVerifier implements AssetVerifier {
  private provider: ethers.providers.JsonRpcProvider;
  
  constructor(provider: ethers.providers.JsonRpcProvider) {
    this.provider = provider;
  }
NFT Ownership Verification
  async verify(assetDetails: AssetDetails, proof: OwnershipProof): Promise<AssetVerificationResult> {
    try {
      // Query on-chain ownership
      const currentOwner = await this.getCurrentOwner(assetDetails);
      const expectedOwner = proof.ownerAddress;
      
      // Compare addresses (case-insensitive)
      if (currentOwner.toLowerCase() !== expectedOwner.toLowerCase()) {
        return {
          verified: false,
          reason: 'On-chain owner does not match claimed owner',
          method: 'blockchain_query',
          confidence: 0
        };
      }
      
      return {
        verified: true,
        reason: 'On-chain ownership confirmed',
        method: 'blockchain_query',
        confidence: 100
      };
      
    } catch (error) {
      return {
        verified: false,
        reason: `Verification failed: ${error.message}`,
        method: 'blockchain_query',
        confidence: 0
      };
    }
  }
Get Current NFT Owner
  private async getCurrentOwner(assetDetails: AssetDetails): Promise<string> {
    const contract = new ethers.Contract(
      assetDetails.contractAddress!,
      ['function ownerOf(uint256 tokenId) view returns (address)'],
      this.provider
    );
    
    return await contract.ownerOf(assetDetails.tokenId);
  }
NFT Metadata Collection
  async getMetadata(assetDetails: AssetDetails): Promise<AssetMetadata> {
    try {
      // Fetch contract-level data
      const contractData = await this.getContractData(assetDetails);
      
      // Fetch token-specific metadata
      const tokenMetadata = await this.getTokenMetadata(contractData.tokenURI);
      
      return {
        uri: contractData.tokenURI,
        contractName: contractData.name,
        contractSymbol: contractData.symbol,
        ...tokenMetadata
      };
      
    } catch (error) {
      console.warn('Failed to get NFT metadata:', error);
      return {};
    }
  }
Contract Data Fetching
  private async getContractData(assetDetails: AssetDetails) {
    const contract = new ethers.Contract(
      assetDetails.contractAddress!,
      [
        'function tokenURI(uint256 tokenId) view returns (string)',
        'function name() view returns (string)',
        'function symbol() view returns (string)'
      ],
      this.provider
    );
    
    const [tokenURI, name, symbol] = await Promise.all([
      contract.tokenURI(assetDetails.tokenId).catch(() => ''),
      contract.name().catch(() => ''),
      contract.symbol().catch(() => '')
    ]);
    
    return { tokenURI, name, symbol };
  }
Token Metadata Fetching
  private async getTokenMetadata(tokenURI: string): Promise<Record<string, any>> {
    // Only fetch from HTTP/HTTPS URLs
    if (!tokenURI || !tokenURI.startsWith('http')) {
      return {};
    }
    
    try {
      const response = await fetch(tokenURI);
      return await response.json();
    } catch (error) {
      console.warn('Failed to fetch metadata from URI:', error);
      return {};
    }
  }
}
Physical Asset Verifier Implementation
class PhysicalAssetVerifier implements AssetVerifier {
  
  async verify(assetDetails: AssetDetails, proof: OwnershipProof): Promise<AssetVerificationResult> {
    // Physical asset verification integrates with:
    // - Government registries (vehicles, real estate)
    // - Manufacturer databases  
    // - Certification authorities
    // - Insurance records
    
    if (!proof.documents || proof.documents.length === 0) {
      return {
        verified: false,
        reason: 'No verification documents provided',
        method: 'document_verification',
        confidence: 0
      };
    }
    
    // Verify ownership documents
    const documentVerification = await this.verifyDocuments(proof.documents);
    
    return {
      verified: documentVerification.valid,
      reason: documentVerification.reason,
      method: 'document_verification',
      confidence: documentVerification.confidence
    };
  }
Physical Asset Metadata
  async getMetadata(assetDetails: AssetDetails): Promise<AssetMetadata> {
    // In production, this would gather metadata from:
    // - Government registries
    // - Manufacturer databases
    // - Valuation services
    // - Condition assessment reports
    
    return {
      assetType: assetDetails.subType,
      condition: 'unknown',
      location: 'unknown',
      estimatedValue: 0
    };
  }
Document Verification
  private async verifyDocuments(documents: OwnershipDocument[]): Promise<{
    valid: boolean;
    reason: string;
    confidence: number;
  }> {
    // In production, this would:
    // 1. Verify document authenticity with issuing authorities
    // 2. Check document tampering/forgery
    // 3. Validate document signatures and seals
    // 4. Cross-reference with official databases
    
    let totalConfidence = 0;
    let validDocuments = 0;
    
    for (const document of documents) {
      const docVerification = await this.verifySingleDocument(document);
      if (docVerification.valid) {
        validDocuments++;
        totalConfidence += docVerification.confidence;
      }
    }
    
    const averageConfidence = validDocuments > 0 ? totalConfidence / validDocuments : 0;
    const allValid = validDocuments === documents.length;
    
    return {
      valid: allValid,
      reason: allValid ? 'All documents verified' : `${validDocuments}/${documents.length} documents verified`,
      confidence: Math.round(averageConfidence)
    };
  }
Single Document Verification
  private async verifySingleDocument(document: OwnershipDocument): Promise<{
    valid: boolean;
    confidence: number;
  }> {
    // Placeholder implementation
    // Real implementation would integrate with document verification APIs
    
    // Basic validation checks
    const hasValidIssuer = document.issuer && document.issuer.length > 0;
    const hasValidHash = document.hash && document.hash.length > 0;
    const isRecentlyIssued = Date.now() - document.issuedDate < (365 * 24 * 60 * 60 * 1000); // 1 year
    
    const validChecks = [hasValidIssuer, hasValidHash, isRecentlyIssued].filter(Boolean).length;
    const confidence = (validChecks / 3) * 85; // Max 85% confidence from basic checks
    
    return {
      valid: validChecks >= 2, // At least 2/3 checks must pass
      confidence: Math.round(confidence)
    };
  }
}

interface AssetDetails {
  assetId: string;
  assetType: 'nft' | 'erc20' | 'physical' | 'custody' | 'real_estate' | 'vehicle';
  subType?: string;
  tokenId?: string;
  contractAddress?: string;
  chainId?: number;
  serialNumber?: string;
  manufacturer?: string;
}

interface OwnershipProof {
  ownerAddress: string;
  proofType: 'blockchain' | 'document' | 'custody' | 'api';
  documents?: OwnershipDocument[];
  signatures?: string[];
  apiKey?: string;
}

interface OwnershipDocument {
  type: string;
  hash: string;
  issuer: string;
  issuedDate: number;
}

interface AssetVerificationResult {
  verified: boolean;
  reason: string;
  method: string;
  confidence: number;
}

interface AssetMetadata {
  uri?: string;
  contractName?: string;
  contractSymbol?: string;
  serialNumber?: string;
  manufacturer?: string;
  condition?: string;
  location?: string;
  estimatedValue?: number;
  marketplace?: string;
  [key: string]: any;
}

interface OwnershipVerificationResult {
  success: boolean;
  sessionId: string;
  attestationUID: string;
  assetMetadata: AssetMetadata;
  verificationMethod: string;
  confidence: number;
}

interface AssetVerifier {
  verify(assetDetails: AssetDetails, proof: OwnershipProof): Promise<AssetVerificationResult>;
  getMetadata(assetDetails: AssetDetails): Promise<AssetMetadata>;
}

Asset Registry Consumer

Implement asset ownership verification in applications:
class AssetRegistryService {
  private sdk: StellarAttestSDK | SolanaAttestSDK;
  
  constructor(sdk: StellarAttestSDK | SolanaAttestSDK) {
    this.sdk = sdk;
  }
Asset Ownership Verification
  async verifyAssetOwnership(
    ownerAddress: string,
    assetId: string,
    assetType: string
  ): Promise<VerifiedAsset | null> {
    try {
      // Fetch ownership attestation
      const attestationData = await this.fetchOwnershipAttestation(ownerAddress, assetType);
      if (!attestationData) return null;
      
      // Validate attestation refers to the correct asset
      if (!this.validateAssetMatch(attestationData.parsed, assetId)) {
        return null;
      }
      
      // Check attestation age
      if (!this.validateAttestationAge(attestationData.parsed)) {
        return null;
      }
      
      // Build verified asset object
      return this.buildVerifiedAsset(ownerAddress, assetId, assetType, attestationData);
      
    } catch (error) {
      console.error('Asset ownership verification failed:', error);
      return null;
    }
  }
Fetch Ownership Attestation
  private async fetchOwnershipAttestation(ownerAddress: string, assetType: string) {
    const schemaUID = this.getSchemaUID(assetType);
    
    const attestation = await this.sdk.fetchAttestation({
      schemaUID,
      subject: ownerAddress
    });
    
    if (attestation.error || !attestation.data || attestation.data.revoked) {
      return null;
    }
    
    return {
      parsed: this.parseAttestationData(attestation.data.value),
      reference: attestation.data.reference
    };
  }
Validation Methods
  private validateAssetMatch(assetData: Record<string, string>, assetId: string): boolean {
    return assetData.assetId === assetId;
  }
  
  private validateAttestationAge(assetData: Record<string, string>): boolean {
    const attestationAge = (Date.now() / 1000) - parseInt(assetData.timestamp);
    const maxAge = 30 * 24 * 60 * 60; // 30 days
    return attestationAge <= maxAge;
  }
Build Verified Asset
  private buildVerifiedAsset(
    ownerAddress: string,
    assetId: string,
    assetType: string,
    attestationData: any
  ): VerifiedAsset {
    const { parsed, reference } = attestationData;
    const maxAge = 30 * 24 * 60 * 60; // 30 days
    
    return {
      assetId,
      assetType,
      owner: ownerAddress,
      verified: parsed.verified === 'true',
      verifiedAt: parseInt(parsed.timestamp),
      attestationUID: reference,
      metadata: this.extractMetadata(parsed),
      expiresAt: parseInt(parsed.timestamp) + maxAge
    };
  }
}
Get All Owner Assets
  async getOwnerAssets(ownerAddress: string): Promise<VerifiedAsset[]> {
    // In production, this would use a proper indexing system
    // to query all attestations by owner across all schemas
    const assetTypes = ['nft-ownership-v1', 'physical-asset-ownership-v1', 'asset-ownership-v1'];
    
    const assetPromises = assetTypes.map(schemaUID => 
      this.fetchAssetsBySchema(ownerAddress, schemaUID)
    );
    
    const assetArrays = await Promise.all(assetPromises);
    return assetArrays.flat().filter(asset => asset !== null);
  }
Fetch Assets by Schema
  private async fetchAssetsBySchema(ownerAddress: string, schemaUID: string): Promise<VerifiedAsset[]> {
    try {
      // This is simplified - production would need proper indexing
      const attestation = await this.sdk.fetchAttestation({
        schemaUID,
        subject: ownerAddress
      });
      
      if (!attestation.data || attestation.data.revoked) {
        return [];
      }
      
      const assetData = this.parseAttestationData(attestation.data.value);
      const asset = this.buildAssetFromAttestation(ownerAddress, schemaUID, assetData, attestation.data.reference);
      
      return [asset];
      
    } catch (error) {
      console.warn(`Failed to fetch assets for schema ${schemaUID}:`, error);
      return [];
    }
  }
Build Asset from Attestation
  private buildAssetFromAttestation(
    ownerAddress: string,
    schemaUID: string,
    assetData: Record<string, string>,
    reference: string
  ): VerifiedAsset {
    const maxAge = 30 * 24 * 60 * 60; // 30 days
    
    return {
      assetId: assetData.assetId,
      assetType: this.schemaToAssetType(schemaUID),
      owner: ownerAddress,
      verified: assetData.verified === 'true',
      verifiedAt: parseInt(assetData.timestamp),
      attestationUID: reference,
      metadata: this.extractMetadata(assetData),
      expiresAt: parseInt(assetData.timestamp) + maxAge
    };
  }
}
Collateral Validation
  async validateCollateral(
    borrowerAddress: string,
    requiredAssets: CollateralRequirement[]
  ): Promise<CollateralValidationResult> {
    // Get all assets owned by borrower
    const ownedAssets = await this.getOwnerAssets(borrowerAddress);
    
    // Check each requirement against owned assets
    const { validCollateral, missingRequirements } = this.matchRequirementsToAssets(
      ownedAssets, requiredAssets
    );
    
    // Calculate total collateral value
    const totalCollateralValue = this.calculateTotalValue(validCollateral);
    
    return {
      valid: missingRequirements.length === 0,
      validCollateral,
      missingRequirements,
      totalValue: totalCollateralValue,
      collateralizationRatio: this.calculateCollateralizationRatio(totalCollateralValue, requiredAssets)
    };
  }
Match Requirements to Assets
  private matchRequirementsToAssets(
    ownedAssets: VerifiedAsset[],
    requiredAssets: CollateralRequirement[]
  ) {
    const validCollateral: VerifiedAsset[] = [];
    const missingRequirements: CollateralRequirement[] = [];
    
    for (const requirement of requiredAssets) {
      const matchingAsset = this.findMatchingAsset(ownedAssets, requirement);
      
      if (matchingAsset) {
        validCollateral.push(matchingAsset);
      } else {
        missingRequirements.push(requirement);
      }
    }
    
    return { validCollateral, missingRequirements };
  }
Find Matching Asset
  private findMatchingAsset(
    ownedAssets: VerifiedAsset[],
    requirement: CollateralRequirement
  ): VerifiedAsset | undefined {
    return ownedAssets.find(asset => {
      // Check asset type match
      if (asset.assetType !== requirement.assetType) return false;
      
      // Check minimum value requirement
      if (requirement.minimumValue && 
          (asset.metadata.estimatedValue || 0) < requirement.minimumValue) {
        return false;
      }
      
      // Check specific asset ID requirement
      if (requirement.specificAssetId && 
          asset.assetId !== requirement.specificAssetId) {
        return false;
      }
      
      return true;
    });
  }
Calculate Total Value
  private calculateTotalValue(assets: VerifiedAsset[]): number {
    return assets.reduce(
      (sum, asset) => sum + (asset.metadata.estimatedValue || 0),
      0
    );
  }


  private getSchemaUID(assetType: string): string {
    const schemaMap = {
      'nft': 'nft-ownership-v1',
      'physical': 'physical-asset-ownership-v1',
      'custody': 'asset-custody-v1',
      'default': 'asset-ownership-v1'
    };
    
    return schemaMap[assetType] || schemaMap.default;
  }

  private schemaToAssetType(schemaUID: string): string {
    const typeMap = {
      'nft-ownership-v1': 'nft',
      'physical-asset-ownership-v1': 'physical',
      'asset-custody-v1': 'custody',
      'asset-ownership-v1': 'digital'
    };
    
    return typeMap[schemaUID] || 'unknown';
  }

  private parseAttestationData(value: string): Record<string, string> {
    const pairs = value.split(',');
    const result: Record<string, string> = {};
    
    pairs.forEach(pair => {
      const [key, val] = pair.split(':');
      result[key] = val;
    });
    
    return result;
  }

  private extractMetadata(assetData: Record<string, string>): AssetMetadata {
    return {
      serialNumber: assetData.serialNumber,
      manufacturer: assetData.manufacturer,
      condition: assetData.condition,
      location: assetData.location,
      estimatedValue: assetData.estimatedValue ? parseInt(assetData.estimatedValue) : undefined,
      contractName: assetData.contractName,
      contractSymbol: assetData.contractSymbol
    };
  }

  private calculateCollateralizationRatio(
    totalValue: number,
    requirements: CollateralRequirement[]
  ): number {
    const requiredValue = requirements.reduce(
      (sum, req) => sum + (req.minimumValue || 0),
      0
    );
    
    return requiredValue > 0 ? (totalValue / requiredValue) * 100 : 0;
  }
}

interface VerifiedAsset {
  assetId: string;
  assetType: string;
  owner: string;
  verified: boolean;
  verifiedAt: number;
  attestationUID: string;
  metadata: AssetMetadata;
  expiresAt: number;
}

interface CollateralRequirement {
  assetType: string;
  minimumValue?: number;
  specificAssetId?: string;
  required: boolean;
}

interface CollateralValidationResult {
  valid: boolean;
  validCollateral: VerifiedAsset[];
  missingRequirements: CollateralRequirement[];
  totalValue: number;
  collateralizationRatio: number;
}

Integration Patterns

NFT Marketplace Integration

Verify NFT ownership for marketplace operations:
class NFTMarketplace {
  private assetRegistry: AssetRegistryService;
  
  constructor(sdk: StellarAttestSDK | SolanaAttestSDK) {
    this.assetRegistry = new AssetRegistryService(sdk);
  }

  async listNFTForSale(
    sellerAddress: string,
    tokenId: string,
    contractAddress: string,
    price: number
  ): Promise<ListingResult> {
    // Verify seller owns the NFT
    const ownership = await this.assetRegistry.verifyAssetOwnership(
      sellerAddress,
      `${contractAddress}:${tokenId}`,
      'nft'
    );
    
    if (!ownership || !ownership.verified) {
      return {
        success: false,
        error: 'NFT ownership could not be verified',
        suggestion: 'Please ensure you own this NFT and it has been properly attested'
      };
    }
    
    // Check if NFT is already listed or under custody
    const custodyCheck = await this.checkCustodyStatus(contractAddress, tokenId);
    if (custodyCheck.inCustody) {
      return {
        success: false,
        error: 'NFT is currently in custody and cannot be listed',
        suggestion: 'Release NFT from custody before listing'
      };
    }
    
    // Create listing with ownership verification
    const listing = await this.createListing({
      tokenId,
      contractAddress,
      seller: sellerAddress,
      price,
      ownershipAttestation: ownership.attestationUID,
      verifiedAt: Date.now()
    });
    
    return {
      success: true,
      listingId: listing.id,
      ownershipVerified: true,
      verificationDetails: {
        attestationUID: ownership.attestationUID,
        verifiedAt: ownership.verifiedAt,
        expiresAt: ownership.expiresAt
      }
    };
  }

  async executePurchase(
    listingId: string,
    buyerAddress: string
  ): Promise<PurchaseResult> {
    const listing = await this.getListing(listingId);
    
    if (!listing) {
      throw new Error('Listing not found');
    }
    
    // Re-verify ownership before purchase
    const currentOwnership = await this.assetRegistry.verifyAssetOwnership(
      listing.seller,
      `${listing.contractAddress}:${listing.tokenId}`,
      'nft'
    );
    
    if (!currentOwnership || !currentOwnership.verified) {
      return {
        success: false,
        error: 'Seller no longer owns the NFT',
        refundRequired: true
      };
    }
    
    // Execute the purchase transaction
    const transaction = await this.processPayment(listing, buyerAddress);
    
    if (transaction.success) {
      // Update ownership attestation to new owner
      await this.transferOwnershipAttestation(
        listing.tokenId,
        listing.contractAddress,
        listing.seller,
        buyerAddress,
        transaction.txHash
      );
    }
    
    return {
      success: transaction.success,
      transactionHash: transaction.txHash,
      newOwnershipAttestation: transaction.newAttestationUID
    };
  }

  private async transferOwnershipAttestation(
    tokenId: string,
    contractAddress: string,
    fromAddress: string,
    toAddress: string,
    txHash: string
  ): Promise<string> {
    // This would typically involve:
    // 1. Revoking the old ownership attestation
    // 2. Creating a new ownership attestation for the buyer
    // 3. Linking the transfer to the blockchain transaction
    
    const oracle = new AssetOwnershipOracle(this.assetRegistry['sdk'], 'ethereum_rpc_url');
    
    const newOwnership = await oracle.verifyAndAttestAssetOwnership(
      toAddress,
      {
        assetId: `${contractAddress}:${tokenId}`,
        assetType: 'nft',
        tokenId,
        contractAddress,
        chainId: 1
      },
      {
        ownerAddress: toAddress,
        proofType: 'blockchain'
      }
    );
    
    return newOwnership.attestationUID;
  }

  private async checkCustodyStatus(contractAddress: string, tokenId: string): Promise<{
    inCustody: boolean;
    custodian?: string;
  }> {
    // Check if NFT is in custody (e.g., staked, lent, etc.)
    return { inCustody: false };
  }

  private async createListing(listingData: any): Promise<{ id: string }> {
    // Create marketplace listing
    return { id: `listing-${Date.now()}` };
  }

  private async getListing(listingId: string): Promise<any> {
    // Get listing details
    return null;
  }

  private async processPayment(listing: any, buyerAddress: string): Promise<{
    success: boolean;
    txHash?: string;
    newAttestationUID?: string;
  }> {
    // Process payment and NFT transfer
    return { success: true, txHash: '0x123...abc' };
  }
}

interface ListingResult {
  success: boolean;
  listingId?: string;
  error?: string;
  suggestion?: string;
  ownershipVerified?: boolean;
  verificationDetails?: {
    attestationUID: string;
    verifiedAt: number;
    expiresAt: number;
  };
}

interface PurchaseResult {
  success: boolean;
  error?: string;
  transactionHash?: string;
  newOwnershipAttestation?: string;
  refundRequired?: boolean;
}

Lending Protocol Integration

Use asset ownership for collateral verification:
class CollateralizedLending {
  private assetRegistry: AssetRegistryService;
  
  constructor(sdk: StellarAttestSDK | SolanaAttestSDK) {
    this.assetRegistry = new AssetRegistryService(sdk);
  }

  async createLoanApplication(
    borrowerAddress: string,
    loanAmount: number,
    collateralAssets: string[],
    loanTerms: LoanTerms
  ): Promise<LoanApplicationResult> {
    // Verify all collateral assets are owned by borrower
    const collateralVerification = await this.verifyCollateralAssets(
      borrowerAddress,
      collateralAssets
    );
    
    if (!collateralVerification.allVerified) {
      return {
        approved: false,
        reason: 'Collateral verification failed',
        details: collateralVerification.issues,
        suggestions: [
          'Ensure all collateral assets are properly attested',
          'Remove unverified assets from collateral list'
        ]
      };
    }
    
    // Calculate loan-to-value ratio
    const ltvRatio = this.calculateLTV(loanAmount, collateralVerification.totalValue);
    
    if (ltvRatio > loanTerms.maxLTV) {
      return {
        approved: false,
        reason: 'Loan-to-value ratio too high',
        details: {
          requestedLTV: ltvRatio,
          maxAllowedLTV: loanTerms.maxLTV,
          collateralValue: collateralVerification.totalValue
        },
        suggestions: [
          'Reduce loan amount',
          'Add more collateral assets',
          'Provide higher-value collateral'
        ]
      };
    }
    
    // Create custody attestations for collateral
    const custodyAttestations = await this.createCustodyAttestations(
      borrowerAddress,
      collateralVerification.assets,
      loanTerms
    );
    
    return {
      approved: true,
      loanId: `loan-${Date.now()}`,
      ltvRatio,
      collateralValue: collateralVerification.totalValue,
      custodyAttestations,
      terms: loanTerms
    };
  }

  private async verifyCollateralAssets(
    borrowerAddress: string,
    assetIds: string[]
  ): Promise<CollateralVerificationResult> {
    const verifiedAssets: VerifiedAsset[] = [];
    const issues: string[] = [];
    let totalValue = 0;
    
    for (const assetId of assetIds) {
      // Parse asset type from ID format
      const assetType = this.parseAssetType(assetId);
      
      const ownership = await this.assetRegistry.verifyAssetOwnership(
        borrowerAddress,
        assetId,
        assetType
      );
      
      if (ownership && ownership.verified) {
        verifiedAssets.push(ownership);
        totalValue += ownership.metadata.estimatedValue || 0;
      } else {
        issues.push(`Asset ${assetId} ownership not verified`);
      }
    }
    
    return {
      allVerified: issues.length === 0,
      assets: verifiedAssets,
      totalValue,
      issues
    };
  }

  private async createCustodyAttestations(
    borrowerAddress: string,
    assets: VerifiedAsset[],
    loanTerms: LoanTerms
  ): Promise<string[]> {
    const custodyAttestations: string[] = [];
    
    for (const asset of assets) {
      try {
        const custodyAttestation = await this.assetRegistry['sdk'].attest({
          schemaUID: 'asset-custody-v1',
          subject: borrowerAddress,
          value: `assetId:${asset.assetId},custodian:${this.getCustodianAddress()},depositor:${borrowerAddress},custodyType:loan_collateral,securityLevel:5,insured:true,depositDate:${Math.floor(Date.now() / 1000)},timestamp:${Math.floor(Date.now() / 1000)}`,
          reference: `custody-${asset.assetId}-${Date.now()}`
        });
        
        if (!custodyAttestation.error) {
          custodyAttestations.push(custodyAttestation.data);
        }
      } catch (error) {
        console.warn(`Failed to create custody attestation for ${asset.assetId}:`, error);
      }
    }
    
    return custodyAttestations;
  }

  private calculateLTV(loanAmount: number, collateralValue: number): number {
    return collateralValue > 0 ? (loanAmount / collateralValue) * 100 : 0;
  }

  private parseAssetType(assetId: string): string {
    if (assetId.includes(':')) {
      return 'nft'; // Format: contract:tokenId
    }
    return 'physical'; // Assume physical asset
  }

  private getCustodianAddress(): string {
    // Return the lending protocol's custodian address
    return 'lending_protocol_custodian_address';
  }
}

interface LoanTerms {
  maxLTV: number;
  interestRate: number;
  duration: number; // days
  liquidationThreshold: number;
}

interface CollateralVerificationResult {
  allVerified: boolean;
  assets: VerifiedAsset[];
  totalValue: number;
  issues: string[];
}

interface LoanApplicationResult {
  approved: boolean;
  loanId?: string;
  reason?: string;
  details?: any;
  suggestions?: string[];
  ltvRatio?: number;
  collateralValue?: number;
  custodyAttestations?: string[];
  terms?: LoanTerms;
}

Best Practices

Security Considerations

Implement robust asset verification:
class SecureAssetVerification {
  static async verifyAssetChain(
    assetId: string,
    expectedOwner: string,
    sdk: StellarAttestSDK | SolanaAttestSDK
  ): Promise<AssetChainVerification> {
    // Verify ownership chain hasn't been broken
    const currentOwnership = await sdk.fetchAttestation({
      schemaUID: 'asset-ownership-v1',
      subject: expectedOwner
    });
    
    if (!currentOwnership.data || currentOwnership.data.revoked) {
      return {
        valid: false,
        reason: 'No current ownership attestation found',
        riskLevel: 'high'
      };
    }
    
    // Check for concurrent ownership claims
    const concurrentClaims = await this.checkConcurrentClaims(assetId, expectedOwner, sdk);
    
    if (concurrentClaims.found) {
      return {
        valid: false,
        reason: 'Multiple ownership claims detected',
        riskLevel: 'critical',
        details: concurrentClaims
      };
    }
    
    // Verify transfer history
    const transferHistory = await this.verifyTransferHistory(assetId, sdk);
    
    return {
      valid: transferHistory.valid,
      reason: transferHistory.reason,
      riskLevel: transferHistory.riskLevel,
      chainLength: transferHistory.transfers.length
    };
  }

  private static async checkConcurrentClaims(
    assetId: string,
    expectedOwner: string,
    sdk: StellarAttestSDK | SolanaAttestSDK
  ): Promise<{ found: boolean; claims?: any[] }> {
    // In a real implementation, this would query for all ownership
    // attestations for this asset across all potential owners
    return { found: false };
  }

  private static async verifyTransferHistory(
    assetId: string,
    sdk: StellarAttestSDK | SolanaAttestSDK
  ): Promise<{
    valid: boolean;
    reason: string;
    riskLevel: 'low' | 'medium' | 'high' | 'critical';
    transfers: any[];
  }> {
    // Verify the complete ownership transfer chain
    return {
      valid: true,
      reason: 'Transfer history verified',
      riskLevel: 'low',
      transfers: []
    };
  }
}

interface AssetChainVerification {
  valid: boolean;
  reason: string;
  riskLevel: 'low' | 'medium' | 'high' | 'critical';
  chainLength?: number;
  details?: any;
}

Privacy Protection

Implement privacy-preserving asset verification:
class PrivateAssetVerification {
  static async createPrivateOwnershipAttestation(
    ownerAddress: string,
    assetValue: number,
    assetCategory: string,
    sdk: StellarAttestSDK | SolanaAttestSDK
  ): Promise<string> {
    // Create attestation with value ranges instead of exact values
    const valueRange = this.categorizeValue(assetValue);
    
    const attestation = await sdk.attest({
      schemaUID: 'private-asset-ownership-v1',
      subject: ownerAddress,
      value: `hasAsset:true,category:${assetCategory},valueRange:${valueRange},verified:true,timestamp:${Math.floor(Date.now() / 1000)}`,
      reference: `private-asset-${Date.now()}`
    });
    
    if (attestation.error) {
      throw new Error(`Private ownership attestation failed: ${attestation.error}`);
    }
    
    return attestation.data;
  }

  private static categorizeValue(value: number): string {
    if (value >= 1000000) return 'very_high';
    if (value >= 100000) return 'high';
    if (value >= 10000) return 'medium';
    if (value >= 1000) return 'low';
    return 'very_low';
  }
}

Next Steps