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:Copy
// 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:Copy
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();
}
Copy
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;
}
}
Copy
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;
}
Copy
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);
}
Copy
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;
}
Copy
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) : {};
}
}
Copy
class NFTVerifier implements AssetVerifier {
private provider: ethers.providers.JsonRpcProvider;
constructor(provider: ethers.providers.JsonRpcProvider) {
this.provider = provider;
}
Copy
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
};
}
}
Copy
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);
}
Copy
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 {};
}
}
Copy
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 };
}
Copy
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 {};
}
}
}
Copy
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
};
}
Copy
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
};
}
Copy
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)
};
}
Copy
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:Copy
class AssetRegistryService {
private sdk: StellarAttestSDK | SolanaAttestSDK;
constructor(sdk: StellarAttestSDK | SolanaAttestSDK) {
this.sdk = sdk;
}
Copy
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;
}
}
Copy
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
};
}
Copy
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;
}
Copy
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
};
}
}
Copy
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);
}
Copy
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 [];
}
}
Copy
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
};
}
}
Copy
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)
};
}
Copy
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 };
}
Copy
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;
});
}
Copy
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:Copy
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:Copy
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:Copy
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:Copy
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';
}
}