Zum Inhalt springen

Einen einfachen Lending- und Borrowing-Smart-Contract auf Linea erstellen

Erfahre, wie du einen einfachen Lending- und Borrowing-Smart-Contract auf Linea erstellst und bereitstellst.

13 Min. Lesezeit
Einen einfachen Lending- und Borrowing-Smart-Contract auf Linea erstellen

Decentralized Finance (DeFi) hat die Art und Weise, wie Menschen Geld verleihen und leihen, grundlegend verändert – durch die Eliminierung von Mittelsmännern und den Einsatz von Smart Contracts zur automatischen Abwicklung von Transaktionen. Was früher Banken und Papierkram erforderte, lässt sich jetzt mit wenigen Klicks vollständig on-chain erledigen.

Linea unterstützt Entwickler beim Aufbau von DeFi-Anwendungen mit verbesserter Skalierbarkeit und niedrigeren Gaskosten im Vergleich zum Ethereum Mainnet – durch den Einsatz von Zero-Knowledge Rollups (zk-Rollups) zur Bündelung und Komprimierung von Transaktionen.

In diesem Leitfaden zeigen wir Schritt für Schritt, wie ein einfacher Lending- und Borrowing-Smart-Contract auf Linea erstellt und deployed wird. Du lernst, wie du Sicherheiten verwaltest, Kredite ausgibst und Rückzahlungsregeln einrichtest – alles wesentliche Funktionen eines DeFi-Lending-Systems.

Am Ende verfügst du über einen funktionierenden Smart Contract und ein solides Verständnis davon, wie DeFi-Lending unter der Haube funktioniert. Egal, ob du neu in der Blockchain-Entwicklung bist oder deine Fähigkeiten weiter ausbauen möchtest – diese Schritt-für-Schritt-Anleitung gibt dir die Werkzeuge, um mit dem Aufbau auf Linea zu beginnen.

Voraussetzungen

  • Du solltest über grundlegende Kenntnisse in Solidity verfügen.

  • Du solltest Nodejs und Foundry auf deinem PC installiert haben.

Übersicht des Contracts

Dieser Contract ermöglicht:

  1. Sicherheitenhinterlegung (Collateral Deposit): Nutzer können Sicherheiten in den Contract einzahlen, wobei die Details sicher im Smart Contract gespeichert werden.

  2. Kreditaufnahme (Borrowing): Nutzer können einen Kredit gegen ihre hinterlegten Sicherheiten aufnehmen.

  3. Rückzahlung (Repayment): Nutzer können ihre Kredite zurückzahlen, um ihre Sicherheiten freizugeben.

  4. Sicherheitenentnahme (Collateral Withdrawal): Nutzer können Sicherheiten abheben, die nicht durch einen aktiven Kredit gesperrt sind.

Projekt-Setup

Für dieses Projekt verwenden wir das Foundry-Framework. Mit installiertem Foundry führe den folgenden Befehl aus, um ein neues Projekt zu initiieren:

forge init lending

Öffne den Market-Ordner in VSCode oder deinem bevorzugten Code-Editor und lösche die Dateien scripts/counter.s.sol, src/counter.sol und test/counter.t.sol.

Alle Abhängigkeiten installieren

forge install foundry-rs/forge-std --no-commit && forge install OpenZeppelin/openzeppelin-contracts --no-commit

Vollständiger Code für den Lending- und Borrowing-Smart-Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
contract LendingBorrowing is Ownable {
    struct Loan {
        uint256 amount; // Borrowed amount
        uint256 collateral; // Collateral amount
        bool isActive; // Loan status
    }
    IERC20 public immutable collateralToken;
    IERC20 public immutable lendingToken;
    uint256 public collateralFactor; // Percentage of collateral allowed to be borrowed (e.g., 50%)
    mapping(address => uint256) public collateralBalances; // User collateral balances
    mapping(address => Loan) public loans; // User loans
    event CollateralDeposited(address indexed user, uint256 amount);
    event CollateralWithdrawn(address indexed user, uint256 amount);
    event LoanTaken(address indexed user, uint256 amount);
    event LoanRepaid(address indexed user, uint256 amount);
    constructor(IERC20 _collateralToken, IERC20 _lendingToken, uint256 _collateralFactor) Ownable(msg.sender) {
        require(_collateralFactor <= 100, "Collateral factor must be <= 100");
        collateralToken = _collateralToken;
        lendingToken = _lendingToken;
        collateralFactor = _collateralFactor;
    }
    function setCollateralFactor(uint256 _newFactor) external onlyOwner {
        require(_newFactor <= 100, "Collateral factor must be <= 100");
        collateralFactor = _newFactor;
    }
    function depositCollateral(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        collateralBalances[msg.sender] += _amount;
        collateralToken.transferFrom(msg.sender, address(this), _amount);
        emit CollateralDeposited(msg.sender, _amount);
    }
    function withdrawCollateral(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        require(collateralBalances[msg.sender] >= _amount, "Insufficient collateral");
        uint256 maxWithdrawable = collateralBalances[msg.sender] - _loanRequiredCollateral(msg.sender);
        require(_amount <= maxWithdrawable, "Cannot withdraw collateral locked for a loan");
        collateralBalances[msg.sender] -= _amount;
        collateralToken.transfer(msg.sender, _amount);
        emit CollateralWithdrawn(msg.sender, _amount);
    }
    function takeLoan(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        require(loans[msg.sender].isActive == false, "Existing loan must be repaid first");
        uint256 maxLoan = (collateralBalances[msg.sender] * collateralFactor) / 100;
        require(_amount <= maxLoan, "Loan exceeds collateral limit");
        loans[msg.sender] = Loan({
            amount: _amount,
            collateral: collateralBalances[msg.sender],
            isActive: true
        });
        lendingToken.transfer(msg.sender, _amount);
        emit LoanTaken(msg.sender, _amount);
    }
    function repayLoan(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        Loan storage userLoan = loans[msg.sender];
        require(userLoan.isActive, "No active loan");
        require(_amount <= userLoan.amount, "Repay amount exceeds loan");
        lendingToken.transferFrom(msg.sender, address(this), _amount);
        userLoan.amount -= _amount;
        if (userLoan.amount == 0) {
            userLoan.isActive = false;
        }
        emit LoanRepaid(msg.sender, _amount);
    }
    function _loanRequiredCollateral(address _user) internal view returns (uint256) {
        Loan memory userLoan = loans[_user];
        if (!userLoan.isActive) return 0;
        return (userLoan.amount * 100) / collateralFactor;
    }
    function getLoanDetails(address _user) external view returns (uint256 amount, uint256 collateral, bool isActive) {
        Loan memory userLoan = loans[_user];
        return (userLoan.amount, userLoan.collateral, userLoan.isActive);
    }
}
Kopieren

Code-Übersicht

Loan Struct

struct Loan {
        uint256 amount; // Borrowed amount
        uint256 collateral; // Collateral amount
        bool isActive; // Loan status
    }
Kopieren

Das Loan-Struct dient als Vorlage zur Speicherung von Details einzelner Kredite, die Nutzer aufgenommen haben. Jedes Feld im Struct repräsentiert eine wichtige Information über den Status und die Konditionen des Kredits. Im Folgenden werden die einzelnen Felder erläutert:

  • Amount: Speichert den Betrag der Token, den der Nutzer geliehen hat. Dieser Wert hilft dabei, den ausstehenden Betrag zu verfolgen, Rückzahlungsverpflichtungen zu bestimmen und Kreditbedingungen zu validieren.

  • Collateral: Speichert den Wert der vom Nutzer hinterlegten Sicherheit zur Absicherung des Kredits. Sie schützt die Mittel des Kreditgebers und hilft dabei, den maximal ausleihbaren Betrag basierend auf dem Collateral Factor zu bestimmen.

  • IsActive: Gibt an, ob der Kredit derzeit aktiv ist. Dieses Feld hilft dabei, den Kreditstatus zu verfolgen und verhindert, dass Nutzer einen neuen Kredit aufnehmen, bevor der bestehende vollständig zurückgezahlt wurde.

State Variables

IERC20 public immutable collateralToken;
    IERC20 public immutable lendingToken;
    uint256 public collateralFactor; // Percentage of collateral allowed to be borrowed (e.g., 50%)
Kopieren

Der obige Ausschnitt definiert drei kritische State Variables, die jeweils eine spezifische Rolle bei der Ermöglichung und Absicherung der Funktionalität der Lending-Plattform spielen. Im Folgenden werden diese Variablen erläutert:

  • CollateralToken: Repräsentiert den Token, den Nutzer als Sicherheit für ihre Kredite hinterlegen. Wenn beispielsweise USDC als Sicherheit verwendet wird, speichert diese Variable die Blockchain-Adresse des USDC-Contracts.

  • LendingToken: Diese Variable repräsentiert den Token, den die Plattform an Kreditnehmer verleiht. Wenn USDT der Lending-Token ist, speichert sie die Blockchain-Adresse des USDT-Contracts.

  • CollateralFactor: Definiert den Prozentsatz des Sicherheitenwerts, den ein Nutzer leihen kann. Er wird als ganze Zahl ausgedrückt, wobei 100 für 100 % des Sicherheitenwerts steht. Wenn der collateralFactor beispielsweise 50 beträgt, kann ein Nutzer bis zu 50 % des Werts seiner Sicherheiten leihen. Dies stellt sicher, dass Kredite überbesichert sind, um die Sicherheit des Lending-Systems zu gewährleisten. Wenn ein Nutzer Sicherheiten im Wert von 1.000 $ hinterlegt und der collateralFactor 50 % beträgt, ist der maximale Kredit, den er aufnehmen kann, 500 $.

Mappings

mapping(address => uint256) public collateralBalances; // User collateral balances
 mapping(address => Loan) public loans; // User loans
Kopieren
  • CollateralBalances Mapping: Verfolgt den Betrag der von jedem Nutzer hinterlegten Sicherheiten. Jeder Nutzer hat ein eindeutiges Sicherheitenguthaben, das mit seiner Adresse verknüpft ist. Dieses Mapping hilft dabei zu validieren, ob ein Nutzer über ausreichend Sicherheiten verfügt, um einen Kredit aufzunehmen oder Mittel abzuheben.

  • Loans Mapping: Verfolgt aktive Kreditdetails für jeden Nutzer, indem seine Adresse mit seinen Kreditinformationen verknüpft wird. Es stellt sicher, dass ein Nutzer keinen neuen Kredit aufnehmen kann, solange er noch einen aktiven hat.

Events

 event CollateralDeposited(address indexed user, uint256 amount); 
 event CollateralWithdrawn(address indexed user, uint256 amount);
 event LoanTaken(address indexed user, uint256 amount);
 event LoanRepaid(address indexed user, uint256 amount);
Kopieren
  • CollateralDeposited Event – Wird ausgelöst, wenn ein Nutzer Sicherheiten in den Contract einzahlt. Es erfasst die Adresse des Nutzers und den eingezahlten Betrag.

  • CollateralWithdrawn Event – Wird ausgelöst, wenn ein Nutzer Sicherheiten aus dem Contract abhebt. Es erfasst die Adresse des Nutzers und den abgehobenen Betrag.

  • LoanTaken Event – Wird ausgelöst, wenn ein Nutzer einen Kredit aufnimmt. Es protokolliert die Adresse des Nutzers und den geliehenen Betrag.

  • LoanRepaid Event – Wird ausgelöst, wenn ein Nutzer einen Kredit zurückzahlt. Es erfasst die Adresse des Nutzers und den Rückzahlungsbetrag.

Constructor

 constructor(IERC20 _collateralToken, IERC20 _lendingToken, uint256 _collateralFactor) Ownable(msg.sender) {
        require(_collateralFactor <= 100, "Collateral factor must be <= 100");
        collateralToken = _collateralToken;
        lendingToken = _lendingToken;
        collateralFactor = _collateralFactor;
    }
Kopieren

Der Constructor ist eine spezielle Funktion in Solidity, die nur einmal während des Contract-Deployments ausgeführt wird. Sein Zweck ist es:

  • Die Anfangskonfiguration des Contracts einzurichten.

  • Schlüssel-State-Variables mit Werten zu belegen.

  • Eigentümerschaft und Zugriffskontrolle über Ownable zu etablieren.

Der Constructor nimmt drei Argumente entgegen: _collateralToken, _lendingToken und _collateralFactor.

  • _collateralToken – Der Token, der als Sicherheit für Kredite verwendet wird.

  • _lendingToken – Der Token, den Nutzer aus dem System leihen.

  • _collateralFactor – Der Prozentsatz der Sicherheiten, der die maximale Kreditkapazität bestimmt und sicherstellt, dass er 100 % nicht überschreitet.

Bei der Initialisierung führt der Constructor folgende Schritte aus:

  • Weist _collateralToken als Sicherheitenasset zu.

  • Setzt _lendingToken als Borrowing-Token.

  • Konfiguriert _collateralFactor, um das Verhältnis von Sicherheit zu Kredit festzulegen.

Zusätzlich stellt Ownable(msg.sender) sicher, dass der Contract-Deployer zum Eigentümer wird und damit administrative Rechte erhält, beispielsweise die Möglichkeit, den collateralFactor bei Bedarf zu ändern.

Collateral Factor festlegen

function setCollateralFactor(uint256 _newFactor) external onlyOwner {
        require(_newFactor <= 100, "Collateral factor must be <= 100");
        collateralFactor = _newFactor;
    }
Kopieren

Diese Funktion ist ein wichtiges administratives Feature, das dem Eigentümer des Contracts ermöglicht, den Collateral Factor zu ändern, der das Verhältnis zwischen Sicherheiten und ausleihbaren Mitteln steuert. Der Collateral Factor wird verwendet, um den maximalen Betrag zu bestimmen, den ein Nutzer gegen seine Sicherheiten leihen kann. Durch die Möglichkeit, diesen Faktor zu aktualisieren, kann sich der Contract an veränderte Marktbedingungen oder Risikoprofile anpassen.

Die Funktion ist als external markiert, was bedeutet, dass sie nur von außerhalb des Contracts aufgerufen werden kann. Der onlyOwner-Modifier stellt sicher, dass nur der Contract-Eigentümer (in der Regel der Deployer) diese Funktion aufrufen kann, was eine strikte Zugriffskontrolle gewährleistet.

Die Funktion stellt sicher, dass der neue Collateral Factor ein gültiger Prozentsatz zwischen 0 und 100 ist, um das Setzen unrealistischer oder unsicherer Werte zu verhindern. Ein Faktor über 100 würde es Nutzern beispielsweise ermöglichen, mehr zu leihen, als ihre Sicherheiten abdecken.

Die Funktion aktualisiert die collateralFactor-State-Variable mit dem neuen Wert, der als _newFactor übergeben wird.

Sicherheiten hinterlegen

 function depositCollateral(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        collateralBalances[msg.sender] += _amount;
        collateralToken.transferFrom(msg.sender, address(this), _amount);
        emit CollateralDeposited(msg.sender, _amount);
    }
Kopieren

Diese Funktion ist ein zentrales Feature, das Nutzern ermöglicht, Sicherheiten in den Contract einzuzahlen. Es ist ein erforderlicher Schritt, bevor ein Nutzer einen Kredit aufnehmen kann, und stellt sicher, dass Sicherheiten ordnungsgemäß erfasst und im Contract gespeichert werden.

Die Funktion validiert, dass der Nutzer einen gültigen Betrag größer als null angibt, und erhöht das Sicherheitenguthaben des Nutzers um den eingezahlten Betrag. Sie verwendet transferFrom, um Token vom Nutzer an den Contract zu übertragen, was erfordert, dass der Nutzer den Contract genehmigt, seine Token auszugeben, bevor er diese Funktion aufruft.

Diese Funktion ermöglicht Folgendes:

  • Sicherheitenhinterlegung: Nutzer zahlen einen bestimmten Betrag eines vordefinierten Tokens (collateralToken) in den Contract ein. Diese Sicherheit dient als Absicherung für alle Kredite, die sie aufnehmen.

  • Verfolgung von Sicherheitenguthaben: Der eingezahlte Betrag wird dem Sicherheitenguthaben des Nutzers hinzugefügt, das im collateralBalances-Mapping verfolgt wird.

  • Kreditaufnahme ermöglichen: Die Sicherheit dient als Grundlage für die Berechnung, wie viel der Nutzer leihen kann.

Sicherheiten abheben

function withdrawCollateral(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        require(collateralBalances[msg.sender] >= _amount, "Insufficient collateral");
        uint256 maxWithdrawable = collateralBalances[msg.sender] - _loanRequiredCollateral(msg.sender);
        require(_amount <= maxWithdrawable, "Cannot withdraw collateral locked for a loan");
        collateralBalances[msg.sender] -= _amount;
        collateralToken.transfer(msg.sender, _amount);
        emit CollateralWithdrawn(msg.sender, _amount);
    }
Kopieren

Diese Funktion ermöglicht es Nutzern, einen Teil oder alle ihrer hinterlegten Sicherheiten abzuheben, wobei sichergestellt wird, dass als Sicherheit für einen aktiven Kredit gesperrte Sicherheiten nicht abgehoben werden können.

Die Funktion validiert, dass der Abhebebetrag größer als null ist, und überprüft, dass der Nutzer über ausreichend verfügbare Sicherheiten verfügt, um die angeforderte Abhebung zu decken. Sie berechnet den Teil der Sicherheiten, der für einen aktiven Kredit gesperrt ist, verringert das Sicherheitenguthaben des Nutzers um den Abhebebetrag und überträgt die angeforderten Token vom Contract zurück an den Nutzer.

Zusammenfassend ermöglicht diese Funktion Folgendes:

  • Sicherheitenentnahme: Ermöglicht Nutzern, ihre verfügbaren Sicherheiten aus dem Contract abzuheben.

  • Kreditschutz: Stellt sicher, dass Nutzer keine Sicherheiten abheben können, die zur Absicherung eines aktiven Kredits benötigt werden.

  • State-Management: Aktualisiert das Sicherheitenguthaben des Nutzers nach einer erfolgreichen Abhebung.

Kredit aufnehmen

function takeLoan(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        require(loans[msg.sender].isActive == false, "Existing loan must be repaid first");
        uint256 maxLoan = (collateralBalances[msg.sender] * collateralFactor) / 100;
        require(_amount <= maxLoan, "Loan exceeds collateral limit");
        loans[msg.sender] = Loan({
            amount: _amount,
            collateral: collateralBalances[msg.sender],
            isActive: true
        });
        lendingToken.transfer(msg.sender, _amount);
        emit LoanTaken(msg.sender, _amount);
    }
Kopieren

Diese Funktion ermöglicht es Nutzern, Token aus dem Contract basierend auf ihren hinterlegten Sicherheiten zu leihen. Der Kreditbetrag wird durch die hinterlegten Sicherheiten des Nutzers und den Collateral Factor des Contracts begrenzt, um sicherzustellen, dass die Kreditaufnahme innerhalb sicherer Grenzen bleibt. Diese Funktion spielt eine Schlüsselrolle bei der Kreditvergabe unter Wahrung der Systemsicherheit.

Die Funktion stellt Folgendes sicher:

  • Der Kreditbetrag ist größer als null.

  • Der Nutzer hat keinen aktiven Kredit, was mehrere gleichzeitige Kredite verhindert.

  • Der angeforderte Kredit überschreitet nicht das maximale Kreditlimit basierend auf den Sicherheiten.

  • Das loans-Mapping wird mit den Kreditdetails des Nutzers aktualisiert.

  • Die geliehenen Token werden vom Contract an den Nutzer übertragen.

Zusammenfassend ermöglicht diese Funktion Folgendes:

  • Kreditvergabe: Ermöglicht Nutzern, Token zu leihen, die durch ihre hinterlegten Sicherheiten gedeckt sind.

  • Sicherheitenverwaltung: Stellt sicher, dass der Kreditbetrag innerhalb der Kreditkapazität des Nutzers bleibt.

  • Kreditverfolgung: Pflegt Nutzerkreditaufzeichnungen, um Überschuldung zu verhindern und die Systemintegrität zu gewährleisten.

Kredit zurückzahlen

function repayLoan(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        Loan storage userLoan = loans[msg.sender];
        require(userLoan.isActive, "No active loan");
        require(_amount <= userLoan.amount, "Repay amount exceeds loan");
        lendingToken.transferFrom(msg.sender, address(this), _amount);
        userLoan.amount -= _amount;
        if (userLoan.amount == 0) {
            userLoan.isActive = false;
        }
        emit LoanRepaid(msg.sender, _amount);
    }
Kopieren

Diese Funktion ermöglicht es Nutzern, ihre aktiven Kredite zurückzuzahlen, entweder teilweise oder vollständig. Sie aktualisiert den Kreditstatus und stellt sicher, dass Rückzahlungen korrekt verarbeitet werden, um die Integrität des Systems zu wahren.

Die Funktion führt folgende Prüfungen und Aktionen durch:

  • Stellt sicher, dass der Rückzahlungsbetrag größer als null ist, um ungültige Operationen zu verhindern.

  • Ruft die Kreditdetails des Nutzers ab und bestätigt, dass er einen aktiven Kredit hat, bevor fortgefahren wird.

  • Stellt sicher, dass der Rückzahlungsbetrag den verbleibenden Kreditsaldo nicht überschreitet.

  • Überträgt den Rückzahlungsbetrag vom Nutzer an den Contract, was eine vorherige Genehmigung für den angegebenen Betrag erfordert.

  • Reduziert den Kreditsaldo um den Rückzahlungsbetrag.

  • Wenn der Kredit vollständig zurückgezahlt ist, markiert die Funktion ihn als inaktiv.

Zusammenfassend ermöglicht diese Funktion Folgendes:

  • Kreditrückzahlung: Ermöglicht Kreditnehmern, den geliehenen Betrag an den Contract zurückzugeben.

  • Kreditverfolgung: Aktualisiert den Kreditsaldo und -status nach der Rückzahlung.

  • Transparenz: Gibt ein Event aus, um Rückzahlungstransaktionen on-chain zu erfassen.

Tests

Erstelle eine Token.sol-Datei im src-Ordner und füge den folgenden Code in die Datei ein.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";

contract Token is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol){}
    function mint(address user, uint256 amount) public {
        _mint(user, amount);
    }
}
Kopieren

Erstelle eine lending.t.sol-Datei im test-Ordner und füge den folgenden Code in die Datei ein.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import { LendingBorrowing} from "../src/Lending.sol";
import { Token } from "../src/Token.sol";
contract LendingTest is Test {
     LendingBorrowing public lending;
     Token public collateral;
     Token public lendingToken;
     address public user1 = makeAddr("user1");
     address public user2 = makeAddr("user2");
     address public user3 = makeAddr("user3");

    function setUp() public {
        collateral = new Token("collateralToken", "ct");
        lendingToken = new Token("lendingToken", "lt");
        lending = new LendingBorrowing(collateral, lendingToken, 80);
        collateral.mint(user1, 100 ether);
        lendingToken.mint(address(lending), 1000 ether);
    }
    function test_DepositCollateral() public {
        vm.startPrank(user1);
        collateral.approve(address(lending), 100 ether);
        lending.depositCollateral(50 ether);

        assert(collateral.balanceOf(user1) == 50 ether);
        assert(collateral.balanceOf(address(lending)) == 50 ether);  
    }

    function test_TakeLoan() public {
        vm.startPrank(user1);
        collateral.approve(address(lending), 100 ether);
        lending.depositCollateral(50 ether);
        vm.expectRevert();
        lending.takeLoan(100 ether);
        vm.expectRevert();
        lending.takeLoan(49 ether);
        lending.takeLoan(40 ether);
        assert(lendingToken.balanceOf(user1) == 40 ether);
    }

    function test_RepayLoan() public {
        vm.startPrank(user1);
        collateral.approve(address(lending), 100 ether);
        lending.depositCollateral(50 ether);
        lending.takeLoan(40 ether);
        lendingToken.approve(address(lending), 40 ether);
        lending.repayLoan(40 ether);
        assert(lendingToken.balanceOf(user1) == 0 ether);
    }

    function test_WithdrawCollateral() public {
        vm.startPrank(user1);
        collateral.approve(address(lending), 100 ether);
        lending.depositCollateral(50 ether);
        lending.takeLoan(40 ether);
         lendingToken.approve(address(lending), 40 ether);
        lending.repayLoan(40 ether);
        lending.withdrawCollateral(50 ether);
        assert(lendingToken.balanceOf(user1) == 0 ether);
        assert(collateral.balanceOf(user1) == 100 ether);
    }

}
Kopieren

Test-Setup

Diese Funktion wird vor jeder Testfunktion aufgerufen und dient zur Initialisierung des Zustands.

  • Sie deployed die Token-Contracts: Sowohl der Collateral- als auch der lendingToken-Contract werden deployed.

  • Sie deployed den LendingBorrowing-Contract: Der LendingBorrowing-Contract wird mit dem Collateral-Token, dem lendingToken und einem Collateral-to-Loan-Verhältnis von 80 % initialisiert (d. h. ein Nutzer kann nur bis zu 80 % seines Sicherheitenwerts leihen).

  • Mint von Token:- 100 Collateral-Token werden für Nutzer 1 geminted.

  • 1000 Lending-Token werden an den Lending-Contract geminted.

Test: Sicherheiten hinterlegen

Diese Funktion testet, dass ein Nutzer Sicherheiten in den Lending-Contract einzahlen kann. Nutzer 1 genehmigt dem Lending-Contract, bis zu 100 seiner Collateral-Token auszugeben, und zahlt 50 Collateral-Token in den Contract ein. Die Funktion überprüft, dass:

  • Das Guthaben von Nutzer 1 um 50 Token reduziert wird.

  • Das Collateral-Guthaben des Lending-Contracts um 50 Token steigt.

Test: Kredit aufnehmen

Diese Funktion testet, dass ein Nutzer einen Kredit basierend auf den von ihm hinterlegten Sicherheiten aufnehmen kann. Nutzer 1 hinterlegt 50 Collateral-Token und genehmigt dem Lending-Contract, diese auszugeben.

  • Der Test stellt sicher, dass die Aufnahme eines Kredits, der den erlaubten Betrag übersteigt oder unter dem erforderlichen Minimum liegt (basierend auf den Sicherheiten), zurückgesetzt wird (reverts).

  • Nutzer 1 nimmt erfolgreich einen Kredit von 40 lendingToken-Token auf.

  • Der Test überprüft, dass Nutzer 1 nach der Kreditaufnahme 40 lendingToken-Token erhält.

Test: Kredit zurückzahlen

Diese Funktion testet, dass ein Nutzer seinen Kredit korrekt zurückzahlen kann.

  • Nutzer 1 hinterlegt 50 Collateral-Token und nimmt einen Kredit von 40 lendingToken-Token auf.

  • Nutzer 1 genehmigt dem Lending-Contract, 40 lendingToken-Token auszugeben, und zahlt den Kredit zurück.

  • Der Test überprüft, dass das lendingToken-Guthaben von Nutzer 1 nach der Kreditrückzahlung null ist.

Test: Sicherheiten abheben

Diese Funktion testet, dass ein Nutzer seine Sicherheiten nach der Rückzahlung des Kredits abheben kann.

  • Nutzer 1 hinterlegt 50 Collateral-Token, nimmt einen Kredit von 40 lendingToken-Token auf und zahlt den Kredit zurück.

  • Nach der Rückzahlung des Kredits hebt Nutzer 1 seine Sicherheiten ab.

  • Der Test überprüft, dass:- Nutzer 1 seine 50 Collateral-Token zurückerhält.

  • Das Guthaben von Nutzer 1 100 beträgt (da er anfänglich 100 Token geminted hat).

  • Das lendingToken-Guthaben von Nutzer 1 nach der Kreditrückzahlung null ist.

Test ausführen

forge test

Deployment und Verifizierung auf Linea

Füge die notwendigen Variablen zur .env-Datei hinzu. Die .env-Datei sollte sich auf der Root-Ebene befinden.

LINEA_RPC_URL=https://linea-mainnet.infura.io/v3/ // Infura RPC endpoint for connecting to Linea 
PRIVATE_KEY=************************************************* // Wallet private key for signing transactions LINEA_API_KEY=************************* // API key for accessing Linea services via Infura
Kopieren

Füge die folgenden Details zur foundry.toml hinzu.

[etherscan]
linea = { key = "${LINEA_API_KEY}", url = "https://api-sepolia.lineascan.build/api" }
Kopieren

Erstelle ein Makefile und füge die folgenden Details hinzu.

deploy:
    forge create src/Lending.sol: LendingBorrowing  --rpc-url $(LINEA_RPC_URL) --private-key $(PRIVATE_KEY)

verify:; forge verify-contract --rpc-url $(LINEA_RPC_URL) --chain linea <contract address> src/Lending.sol:LendingBorrowing --constructor-args arg1 arg2
Kopieren

Deployment

make deploy

Verifizierung

make verify

Fazit

Das Erstellen eines einfachen Lending- und Borrowing-Smart-Contracts legt das Fundament für das Verständnis von DeFi und Blockchain-Entwicklung. Dieser Leitfaden hat Folgendes behandelt:

  • Sicherheiten hinterlegen – Nutzer sperren Assets als Sicherheit für Kredite.

  • Kredite aufnehmen – Kreditnehmer erhalten Zugang zu Mitteln basierend auf ihren Sicherheiten.

  • Kredite zurückzahlen – Nutzer geben geliehene Mittel zurück, um ihre Schulden zu begleichen.

  • Sicherheiten abheben – Nutzer fordern ihre Sicherheiten nach vollständiger Rückzahlung zurück.

Durch die Integration dieser Elemente haben wir ein funktionales, automatisiertes Lending-System erstellt. Mit dem Wachstum von DeFi dienen diese Konzepte als Bausteine für fortschrittlichere Finanzanwendungen.

Für weitere praxisorientierte Tutorials und tiefgehende Einblicke in die Blockchain-Entwicklung besuche den MetaMask Developer YouTube-Kanal und bleibe mit unseren neuesten Erkenntnissen auf dem Laufenden, indem du uns auf Twitter folgst.

KI-übersetzt. Kann Fehler enthalten. Bitte überprüfen Sie die Informationen stets.

Bewerten Sie die Übersetzung

Dieser Artikel wurde verfasst von:

  • Kingsley Okonkwo

    Kingsley, zertifizierter Ethereum-Blockchain-Entwickler, ist als technischer Redakteur bei Consensys tätig und hat sich auf Entwickler-Tools spezialisiert. Vor seinem Einstieg bei Consensys arbeitete Kingsley als freiberuflicher Backend-Entwickler auf verschiedenen Plattformen, darunter Gigster und Braintrust. Mit 5 Jahren Erfahrung in der Softwareentwicklung beherrscht er Tools wie JavaScript (Node.js & React), GoLang, SQL und Docker. Kingsley zeichnet sich durch die Erstellung von Schritt-für-Schritt-Tutorials und Anleitungen aus und nutzt dabei die neuesten Web3-Technologien, um anderen Entwicklern beim Aufbau besserer Dapps zu helfen. Derzeit lebt Kingsley in Dubai und verbringt seine Freizeit gerne mit Basketball und Fußball.

    Alle Artikel lesen