Skip to main content

Overview

This example demonstrates how to integrate IOTA Repstation into a DeFi lending platform to create a comprehensive credit scoring system. The platform uses reputation data to assess borrower creditworthiness, set interest rates, and determine loan limits.

Architecture

Credit Score Components

The lending platform combines multiple reputation factors:
  • Global Reputation: Cross-platform trustworthiness
  • Lending History: Previous loan performance within the platform
  • Transaction Volume: Historical deal activity and amounts
  • Category Performance: Specific performance in financial categories
  • Recency Weight: More recent ratings have higher impact

Platform Integration

1. Setup Credit Scoring Engine

// credit-engine.ts
import { RepstationClient } from '@repstation/sdk';

interface CreditProfile {
  walletAddress: string;
  creditScore: number;        // 300-850 (traditional credit range)
  riskTier: 'AAA' | 'AA' | 'A' | 'BBB' | 'BB' | 'B' | 'C';
  maxLoanAmount: number;
  interestRateAdjustment: number; // Basis points adjustment
  reputation: {
    global: { average: number; count: number };
    lending: { average: number; count: number };
  };
}

class CreditScoringEngine {
  private reputation: RepstationClient;
  
  constructor() {
    this.reputation = new RepstationClient({
      network: 'mainnet',
      packageId: process.env.REPSTATION_PACKAGE_ID!
    });
  }
  
  async calculateCreditScore(walletAddress: string): Promise<CreditProfile> {
    // Get reputation profile
    const reputationResult = await this.reputation.getReputationProfile({
      walletAddress
    });
    
    if (!reputationResult.success || !reputationResult.profile) {
      return this.getDefaultProfile(walletAddress);
    }
    
    const profile = reputationResult.profile;
    
    // Extract lending-specific reputation
    const lendingReputation = profile.appScoped.find(
      app => app.appOwner === process.env.LENDING_APP_OWNER
    );
    
    // Calculate credit score (300-850 range)
    const creditScore = this.calculateScore(profile, lendingReputation);
    
    // Determine risk tier
    const riskTier = this.getRiskTier(creditScore);
    
    // Calculate loan limits and rates
    const maxLoanAmount = this.calculateMaxLoan(creditScore, profile);
    const interestRateAdjustment = this.calculateRateAdjustment(riskTier);
    
    return {
      walletAddress,
      creditScore,
      riskTier,
      maxLoanAmount,
      interestRateAdjustment,
      reputation: {
        global: profile.global,
        lending: lendingReputation?.stats || { average: 0, count: 0 }
      }
    };
  }
  
  private calculateScore(
    profile: any, 
    lendingReputation?: any
  ): number {
    const baseScore = 500; // Starting point
    
    // Global reputation factor (0-150 points)
    const globalFactor = Math.min(
      (profile.global.average / 100) * 150, 
      150
    );
    
    // Lending history factor (0-100 points)
    const lendingFactor = lendingReputation 
      ? Math.min((lendingReputation.stats.average / 100) * 100, 100)
      : 0;
    
    // Activity factor based on rating count (0-100 points)
    const activityFactor = Math.min(
      Math.log(profile.global.count + 1) * 20, 
      100
    );
    
    // Consistency bonus for lending reputation (0-50 points)
    const consistencyBonus = lendingReputation && lendingReputation.stats.count >= 5
      ? Math.min(lendingReputation.stats.average >= 80 ? 50 : 25, 50)
      : 0;
    
    const totalScore = Math.round(
      baseScore + globalFactor + lendingFactor + activityFactor + consistencyBonus
    );
    
    return Math.min(Math.max(totalScore, 300), 850); // Clamp to valid range
  }
  
  private getRiskTier(creditScore: number): CreditProfile['riskTier'] {
    if (creditScore >= 800) return 'AAA';
    if (creditScore >= 750) return 'AA';
    if (creditScore >= 700) return 'A';
    if (creditScore >= 650) return 'BBB';
    if (creditScore >= 600) return 'BB';
    if (creditScore >= 550) return 'B';
    return 'C';
  }
  
  private calculateMaxLoan(creditScore: number, profile: any): number {
    const baseLoan = 1000; // $1000 base loan
    const scoreMultiplier = (creditScore - 300) / 550; // 0-1 range
    const activityMultiplier = Math.min(Math.log(profile.global.count + 1) / 5, 3);
    
    return Math.round(baseLoan * (1 + scoreMultiplier * 10) * activityMultiplier);
  }
  
  private calculateRateAdjustment(riskTier: CreditProfile['riskTier']): number {
    // Basis points adjustment to base interest rate
    const adjustments = {
      'AAA': -200, // 2% discount
      'AA': -100,  // 1% discount
      'A': -50,    // 0.5% discount
      'BBB': 0,    // No adjustment
      'BB': 100,   // 1% premium
      'B': 250,    // 2.5% premium
      'C': 500     // 5% premium
    };
    
    return adjustments[riskTier];
  }
  
  private getDefaultProfile(walletAddress: string): CreditProfile {
    return {
      walletAddress,
      creditScore: 500, // Neutral score for new users
      riskTier: 'BBB',
      maxLoanAmount: 1000,
      interestRateAdjustment: 200, // 2% premium for unknown users
      reputation: {
        global: { average: 0, count: 0 },
        lending: { average: 0, count: 0 }
      }
    };
  }
}

2. Loan Application Process

// loan-service.ts
import { CreditScoringEngine } from './credit-engine';

interface LoanApplication {
  borrower: string;
  amount: number;
  duration: number; // days
  purpose: string;
  collateralAddress?: string;
}

interface LoanOffer {
  maxAmount: number;
  interestRate: number;
  duration: number;
  creditProfile: CreditProfile;
  terms: string[];
}

class LoanService {
  private creditEngine: CreditScoringEngine;
  private reputation: RepstationClient;
  
  constructor() {
    this.creditEngine = new CreditScoringEngine();
    this.reputation = new RepstationClient({
      network: 'mainnet',
      packageId: process.env.REPSTATION_PACKAGE_ID!
    });
  }
  
  async assessLoanApplication(application: LoanApplication): Promise<LoanOffer> {
    // Get credit profile
    const creditProfile = await this.creditEngine.calculateCreditScore(
      application.borrower
    );
    
    // Calculate interest rate
    const baseRate = 0.08; // 8% base APR
    const adjustedRate = baseRate + (creditProfile.interestRateAdjustment / 10000);
    
    // Determine max loan amount
    const maxAmount = Math.min(
      application.amount,
      creditProfile.maxLoanAmount
    );
    
    // Generate loan terms
    const terms = this.generateLoanTerms(creditProfile, application);
    
    return {
      maxAmount,
      interestRate: adjustedRate,
      duration: application.duration,
      creditProfile,
      terms
    };
  }
  
  async approveLoan(
    application: LoanApplication, 
    offer: LoanOffer
  ): Promise<string> {
    // Create reputation deal for the loan
    const dealResult = await this.reputation.openDealAdmin({
      signer: process.env.LENDING_ADMIN_WALLET!,
      admin_cap_id: process.env.LENDING_ADMIN_CAP!,
      party_b: application.borrower,
      subject_ref: {
        type: 'LOAN',
        id: `loan-${Date.now()}`
      },
      amount: offer.maxAmount.toString()
    });
    
    if (!dealResult.success) {
      throw new Error('Failed to create reputation deal');
    }
    
    // Store loan in database
    const loan = await this.createLoanRecord({
      ...application,
      amount: offer.maxAmount,
      interestRate: offer.interestRate,
      reputationDealId: dealResult.deal_id!,
      creditScore: offer.creditProfile.creditScore,
      riskTier: offer.creditProfile.riskTier
    });
    
    return loan.id;
  }
  
  private generateLoanTerms(
    creditProfile: CreditProfile, 
    application: LoanApplication
  ): string[] {
    const terms = [
      `Credit Score: ${creditProfile.creditScore} (${creditProfile.riskTier})`,
      `Maximum loan amount: $${creditProfile.maxLoanAmount.toLocaleString()}`,
      `Interest rate adjustment: ${creditProfile.interestRateAdjustment >= 0 ? '+' : ''}${creditProfile.interestRateAdjustment} basis points`
    ];
    
    // Add reputation-based terms
    if (creditProfile.reputation.global.count === 0) {
      terms.push('New user: Additional verification required');
      terms.push('Collateral requirement: 120% of loan value');
    } else if (creditProfile.creditScore >= 750) {
      terms.push('High credit score: Reduced collateral requirement (110%)');
      terms.push('Fast approval: Decision within 24 hours');
    } else if (creditProfile.creditScore < 600) {
      terms.push('Enhanced monitoring: Weekly check-ins required');
      terms.push('Higher collateral requirement: 150% of loan value');
    }
    
    return terms;
  }
}

3. Loan Lifecycle Management

// loan-lifecycle.ts
class LoanLifecycleManager {
  private reputation: RepstationClient;
  
  constructor() {
    this.reputation = new RepstationClient({
      network: 'mainnet',
      packageId: process.env.REPSTATION_PACKAGE_ID!
    });
  }
  
  async activateLoan(loanId: string, borrowerSignature: string): Promise<void> {
    const loan = await this.getLoan(loanId);
    
    // Accept the reputation deal
    await this.reputation.acceptDeal({
      signer: loan.borrower,
      deal_id: loan.reputationDealId
    });
    
    // Update loan status
    await this.updateLoanStatus(loanId, 'ACTIVE');
    
    // Disburse funds
    await this.disburseFunds(loan);
  }
  
  async completeLoanRepayment(loanId: string): Promise<void> {
    const loan = await this.getLoan(loanId);
    
    // Close the reputation deal
    await this.reputation.closeDeal({
      signer: process.env.LENDING_ADMIN_WALLET!,
      deal_id: loan.reputationDealId
    });
    
    // Update loan status
    await this.updateLoanStatus(loanId, 'COMPLETED');
    
    // Automatically rate successful repayment
    await this.rateSuccessfulRepayment(loan);
  }
  
  async handleLoanDefault(loanId: string): Promise<void> {
    const loan = await this.getLoan(loanId);
    
    // Close the reputation deal
    await this.reputation.closeDeal({
      signer: process.env.LENDING_ADMIN_WALLET!,
      deal_id: loan.reputationDealId
    });
    
    // Update loan status
    await this.updateLoanStatus(loanId, 'DEFAULTED');
    
    // Rate the default negatively
    await this.rateDefault(loan);
  }
  
  private async rateSuccessfulRepayment(loan: any): Promise<void> {
    const paymentScore = this.calculatePaymentScore(loan);
    
    // Rate multiple aspects of the loan performance
    const categories = [
      { name: 'repayment_reliability', score: paymentScore },
      { name: 'communication', score: loan.communicationScore || 85 },
      { name: 'loan_compliance', score: loan.complianceScore || 90 }
    ];
    
    for (const category of categories) {
      await this.reputation.rate({
        signer: process.env.LENDING_ADMIN_WALLET!,
        deal_id: loan.reputationDealId,
        score: category.score,
        category: category.name
      });
    }
  }
  
  private async rateDefault(loan: any): Promise<void> {
    // Rate default with low scores
    await this.reputation.rate({
      signer: process.env.LENDING_ADMIN_WALLET!,
      deal_id: loan.reputationDealId,
      score: 20, // Very low score for default
      category: 'repayment_reliability'
    });
    
    await this.reputation.rate({
      signer: process.env.LENDING_ADMIN_WALLET!,
      deal_id: loan.reputationDealId,
      score: 30,
      category: 'loan_compliance'
    });
  }
  
  private calculatePaymentScore(loan: any): number {
    const daysLate = loan.totalDaysLate || 0;
    const baseScore = 95;
    
    // Deduct points for late payments
    const latePaymentPenalty = Math.min(daysLate * 2, 40);
    
    // Bonus for early repayment
    const earlyRepaymentBonus = loan.earlyRepayment ? 5 : 0;
    
    return Math.max(baseScore - latePaymentPenalty + earlyRepaymentBonus, 20);
  }
}

4. Credit Dashboard Component

// components/CreditDashboard.tsx
import React, { useState, useEffect } from 'react';
import { CreditScoringEngine } from '../services/credit-engine';

interface CreditDashboardProps {
  walletAddress: string;
}

export function CreditDashboard({ walletAddress }: CreditDashboardProps) {
  const [creditProfile, setCreditProfile] = useState<CreditProfile | null>(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    loadCreditProfile();
  }, [walletAddress]);
  
  const loadCreditProfile = async () => {
    try {
      const creditEngine = new CreditScoringEngine();
      const profile = await creditEngine.calculateCreditScore(walletAddress);
      setCreditProfile(profile);
    } catch (error) {
      console.error('Failed to load credit profile:', error);
    } finally {
      setLoading(false);
    }
  };
  
  if (loading) {
    return <div className="loading">Loading credit profile...</div>;
  }
  
  if (!creditProfile) {
    return <div className="error">Failed to load credit profile</div>;
  }
  
  return (
    <div className="credit-dashboard">
      <div className="credit-score-display">
        <div className="score-circle">
          <span className="score">{creditProfile.creditScore}</span>
          <span className="label">Credit Score</span>
        </div>
        <div className="risk-tier">
          <span className={`tier ${creditProfile.riskTier.toLowerCase()}`}>
            {creditProfile.riskTier}
          </span>
        </div>
      </div>
      
      <div className="credit-details">
        <div className="detail-card">
          <h3>Loan Capacity</h3>
          <p className="amount">${creditProfile.maxLoanAmount.toLocaleString()}</p>
          <p className="description">Maximum loan amount available</p>
        </div>
        
        <div className="detail-card">
          <h3>Interest Rate</h3>
          <p className="rate">
            {creditProfile.interestRateAdjustment >= 0 ? '+' : ''}
            {(creditProfile.interestRateAdjustment / 100).toFixed(2)}%
          </p>
          <p className="description">Adjustment to base rate</p>
        </div>
        
        <div className="detail-card">
          <h3>Global Reputation</h3>
          <p className="score">{creditProfile.reputation.global.average}/100</p>
          <p className="count">({creditProfile.reputation.global.count} ratings)</p>
        </div>
        
        <div className="detail-card">
          <h3>Lending History</h3>
          <p className="score">{creditProfile.reputation.lending.average}/100</p>
          <p className="count">({creditProfile.reputation.lending.count} loans)</p>
        </div>
      </div>
      
      <div className="improvement-tips">
        <h3>Improve Your Credit Score</h3>
        <CreditImprovementTips creditProfile={creditProfile} />
      </div>
    </div>
  );
}

function CreditImprovementTips({ creditProfile }: { creditProfile: CreditProfile }) {
  const tips = [];
  
  if (creditProfile.reputation.global.count < 5) {
    tips.push({
      title: "Build Transaction History",
      description: "Complete more deals on integrated platforms to build your reputation",
      impact: "+20-40 points"
    });
  }
  
  if (creditProfile.reputation.lending.count === 0) {
    tips.push({
      title: "Start with Small Loans",
      description: "Successfully complete smaller loans to build lending-specific reputation",
      impact: "+30-50 points"
    });
  }
  
  if (creditProfile.reputation.global.average < 80) {
    tips.push({
      title: "Improve Service Quality",
      description: "Focus on excellent communication and timely delivery in your transactions",
      impact: "+10-30 points"
    });
  }
  
  return (
    <div className="tips-list">
      {tips.map((tip, index) => (
        <div key={index} className="tip-card">
          <h4>{tip.title}</h4>
          <p>{tip.description}</p>
          <span className="impact">Potential impact: {tip.impact}</span>
        </div>
      ))}
    </div>
  );
}

5. Loan Application Form

// components/LoanApplicationForm.tsx
import React, { useState } from 'react';
import { LoanService } from '../services/loan-service';
import { CreditDashboard } from './CreditDashboard';

export function LoanApplicationForm({ walletAddress }: { walletAddress: string }) {
  const [application, setApplication] = useState({
    amount: '',
    duration: '30',
    purpose: '',
    collateralAddress: ''
  });
  const [offer, setOffer] = useState<LoanOffer | null>(null);
  const [loading, setLoading] = useState(false);
  
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    
    try {
      const loanService = new LoanService();
      const loanOffer = await loanService.assessLoanApplication({
        borrower: walletAddress,
        amount: parseInt(application.amount),
        duration: parseInt(application.duration),
        purpose: application.purpose,
        collateralAddress: application.collateralAddress || undefined
      });
      
      setOffer(loanOffer);
    } catch (error) {
      console.error('Failed to assess loan application:', error);
    } finally {
      setLoading(false);
    }
  };
  
  const handleAcceptOffer = async () => {
    if (!offer) return;
    
    try {
      const loanService = new LoanService();
      const loanId = await loanService.approveLoan(
        {
          borrower: walletAddress,
          amount: parseInt(application.amount),
          duration: parseInt(application.duration),
          purpose: application.purpose
        },
        offer
      );
      
      alert(`Loan approved! Loan ID: ${loanId}`);
    } catch (error) {
      console.error('Failed to approve loan:', error);
    }
  };
  
  return (
    <div className="loan-application">
      <CreditDashboard walletAddress={walletAddress} />
      
      <form onSubmit={handleSubmit} className="application-form">
        <h2>Apply for a Loan</h2>
        
        <div className="form-group">
          <label>Loan Amount ($)</label>
          <input
            type="number"
            value={application.amount}
            onChange={(e) => setApplication({...application, amount: e.target.value})}
            placeholder="Enter amount"
            required
          />
        </div>
        
        <div className="form-group">
          <label>Duration (days)</label>
          <select
            value={application.duration}
            onChange={(e) => setApplication({...application, duration: e.target.value})}
          >
            <option value="30">30 days</option>
            <option value="60">60 days</option>
            <option value="90">90 days</option>
            <option value="180">180 days</option>
          </select>
        </div>
        
        <div className="form-group">
          <label>Purpose</label>
          <textarea
            value={application.purpose}
            onChange={(e) => setApplication({...application, purpose: e.target.value})}
            placeholder="Describe the purpose of this loan"
            rows={3}
            required
          />
        </div>
        
        <div className="form-group">
          <label>Collateral Address (Optional)</label>
          <input
            type="text"
            value={application.collateralAddress}
            onChange={(e) => setApplication({...application, collateralAddress: e.target.value})}
            placeholder="0x..."
          />
        </div>
        
        <button type="submit" disabled={loading}>
          {loading ? 'Assessing...' : 'Get Loan Offer'}
        </button>
      </form>
      
      {offer && (
        <div className="loan-offer">
          <h3>Loan Offer</h3>
          
          <div className="offer-details">
            <div className="detail">
              <label>Approved Amount:</label>
              <span>${offer.maxAmount.toLocaleString()}</span>
            </div>
            
            <div className="detail">
              <label>Interest Rate:</label>
              <span>{(offer.interestRate * 100).toFixed(2)}% APR</span>
            </div>
            
            <div className="detail">
              <label>Credit Score:</label>
              <span>{offer.creditProfile.creditScore} ({offer.creditProfile.riskTier})</span>
            </div>
          </div>
          
          <div className="loan-terms">
            <h4>Terms & Conditions</h4>
            <ul>
              {offer.terms.map((term, index) => (
                <li key={index}>{term}</li>
              ))}
            </ul>
          </div>
          
          <div className="offer-actions">
            <button onClick={handleAcceptOffer} className="accept-btn">
              Accept Offer
            </button>
            <button onClick={() => setOffer(null)} className="decline-btn">
              Decline
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

Key Benefits

For Lenders

  • Risk Assessment: Comprehensive credit scoring using blockchain reputation
  • Automated Underwriting: Reputation-based loan approval process
  • Default Prediction: Historical performance indicators
  • Cross-Platform Insights: Global user behavior analysis

For Borrowers

  • Portable Credit History: Reputation follows across platforms
  • Transparent Scoring: Clear understanding of credit factors
  • Improved Access: Good reputation enables better loan terms
  • Incentive Alignment: Motivation to maintain positive reputation

For the Ecosystem

  • Reduced Information Asymmetry: Shared reputation data
  • Lower Default Rates: Better risk assessment
  • Financial Inclusion: Credit access for underbanked users
  • Standardized Scoring: Consistent metrics across platforms

Implementation Considerations

Risk Management

  • Implement minimum reputation thresholds
  • Use collateral requirements based on credit scores
  • Monitor borrower behavior during loan terms
  • Implement early warning systems for potential defaults

Regulatory Compliance

  • Ensure compliance with local lending regulations
  • Implement proper KYC/AML procedures
  • Maintain audit trails for all transactions
  • Consider data privacy requirements

Security

  • Secure wallet integration for signatures
  • Multi-signature approval for large loans
  • Smart contract audits for loan terms
  • Proper key management for admin operations
This lending platform example demonstrates how IOTA Repstation can revolutionize DeFi lending by providing transparent, portable credit scoring that benefits both lenders and borrowers while reducing systemic risk.