Cryptocurrency Defi Ethereum Smart Contracts Solidity Web3
Ranjithkumar  

Building a Decentralized Lending Platform Using ERC20

The world of decentralized finance (DeFi) has transformed how we think about financial systems, breaking the boundaries of traditional banking by enabling peer-to-peer transactions without intermediaries. One of the key components of DeFi is decentralized lending platforms, where users can lend and borrow digital assets directly through smart contracts. These platforms eliminate the need for middlemen and offer faster, cheaper, and more secure alternatives to traditional loans.

In this blog, we’ll explore how to build a decentralized lending platform using ERC20, the standard for creating tokens on Ethereum. You’ll learn how smart contracts work behind the scenes to manage loans, handle collateral, and maintain fairness between lenders and borrowers—all in a decentralized, transparent way. Whether you’re a developer interested in decentralized solutions or someone curious about the mechanics of DeFi, this post will guide you through the essential steps to implement your own decentralized lending platform.

So let’s dive into the decentralized future!

What is a Decentralized Lending Platform?

A decentralized lending platform is a financial system built on blockchain technology that allows users to lend and borrow digital assets without the need for intermediaries like banks or financial institutions. Instead of relying on a centralized authority, these platforms use smart contracts—self-executing contracts with the terms of the agreement directly written into code. This trustless, peer-to-peer environment ensures that transactions are secure, transparent, and autonomous.

Key Features:

  • No Intermediaries: Lenders and borrowers interact directly through smart contracts.
  • Permissionless: Anyone with an internet connection and crypto wallet can participate.
  • Transparent & Secure: All transactions are recorded on the blockchain, making them tamper-proof.
  • Global Access: Users from anywhere in the world can lend or borrow without worrying about traditional financial barriers.

How Does a Decentralized Lending Platform Work?

Here’s a high-level breakdown of how decentralized lending platforms typically operate:

  1. Depositing Assets (Lenders): Lenders deposit their digital assets (e.g., ETH, ERC20 tokens) into the lending platform’s liquidity pool. These assets become available for borrowing by others on the platform. Lenders, in return, earn interest over time based on the demand for the assets they’ve supplied.
  2. Borrowing Against Collateral (Borrowers): Borrowers can access loans by locking up collateral—usually other cryptocurrencies—in the form of smart contracts. The value of the collateral is typically higher than the loan to protect the lender in case the borrower defaults (this is called overcollateralization).
  3. Interest Rates & Liquidation: Interest rates are often determined algorithmically, depending on supply and demand for the tokens in the liquidity pool. If the value of the collateral falls below a certain threshold (due to market volatility), the smart contract will automatically liquidate the collateral to repay the loan, ensuring lenders don’t lose their assets.
  4. Repaying Loans: Borrowers can repay their loans at any time, including the interest accrued. Once the loan is repaid, the collateral is unlocked and returned to the borrower. If the borrower fails to repay the loan, the collateral remains with the lender as a form of insurance.

Example Flow:

  • Alice has extra DAI (a stablecoin) and wants to earn passive income, so she deposits it into a decentralized lending platform.
  • Bob needs a loan and is willing to put up some ETH as collateral. He locks his ETH into a smart contract and borrows DAI.
  • Bob pays interest on the DAI while Alice earns a portion of it.
  • If Bob’s collateral value falls due to ETH price dropping, the smart contract liquidates enough ETH to cover the loan, keeping Alice’s funds safe.

This automated, transparent system eliminates traditional banking overheads, giving users more control over their assets and providing faster, borderless lending and borrowing opportunities.

Implementation of a Decentralized Lending Platform

Here’s an overview of the key smart contracts involved in a decentralized lending platform and how they interact:

1. ERC20 Token Contract

The first and most essential component is the ERC20 token contract. ERC20 is the standard for creating fungible tokens on the Ethereum network, ensuring that they can interact with other decentralized applications (DApps).

In a lending platform, tokens like DAI, USDC, or any other ERC20 tokens can be lent or borrowed. The ERC20 token contract defines:

  • Token supply
  • Transfer of tokens between users (e.g., borrower, lender)
  • Approvals and balances for lending/borrowing
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract LendingToken is ERC20 {
    constructor() ERC20("LendingToken", "LNT") {
        _mint(msg.sender, 1000000 * 10 ** decimals());  // Mint initial supply to the owner
    }
}

This contract ensures compatibility and seamless integration with the lending platform.

2. Lending Pool Contract

The Lending Pool Contract is responsible for managing the assets deposited by lenders and the loans provided to borrowers. This contract holds the total liquidity for a particular token, and it keeps track of the lending balance and interest accrued.

Key Functions:

  • Deposit Tokens: Lenders deposit their ERC20 tokens into the lending pool. In return, they receive interest-bearing tokens (like aTokens in Aave) representing their deposit and interest entitlement.
  • Withdraw Tokens: Lenders can withdraw their tokens plus accrued interest when they want to exit the pool.
  • Borrow Tokens: Borrowers can request loans from the pool, but only if they’ve provided enough collateral (explained in the next contract).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract LendingPool {
    IERC20 public lendingToken;

    mapping(address => uint256) public depositedAmounts;
    mapping(address => uint256) public borrowedAmounts;

    uint256 public totalLiquidity;
    
    constructor(address _tokenAddress) {
        lendingToken = IERC20(_tokenAddress);
    }

    // Lender deposits tokens
    function deposit(uint256 _amount) external {
        require(_amount > 0, "Amount should be greater than zero");
        lendingToken.transferFrom(msg.sender, address(this), _amount);
        depositedAmounts[msg.sender] += _amount;
        totalLiquidity += _amount;
    }

    // Lender withdraws tokens
    function withdraw(uint256 _amount) external {
        require(depositedAmounts[msg.sender] >= _amount, "Insufficient balance");
        lendingToken.transfer(msg.sender, _amount);
        depositedAmounts[msg.sender] -= _amount;
        totalLiquidity -= _amount;
    }

    // Borrower borrows tokens (to be used with collateral)
    function borrow(uint256 _amount) external {
        require(totalLiquidity >= _amount, "Not enough liquidity");
        borrowedAmounts[msg.sender] += _amount;
        totalLiquidity -= _amount;
        lendingToken.transfer(msg.sender, _amount);
    }

    // Repay borrowed tokens
    function repay(uint256 _amount) external {
        require(borrowedAmounts[msg.sender] >= _amount, "Cannot repay more than borrowed");
        borrowedAmounts[msg.sender] -= _amount;
        totalLiquidity += _amount;
        lendingToken.transferFrom(msg.sender, address(this), _amount);
    }
}

The Lending Pool Contract ensures that lending and borrowing requests are handled fairly, and balances are always accurate.

3. Collateral Management Contract

This contract manages the collateral that borrowers need to lock up to receive loans. In decentralized lending platforms, loans are typically overcollateralized to protect lenders from default.

Key Functions:

  • Deposit Collateral: Borrowers must lock collateral (often in the form of other cryptocurrencies like ETH or BTC) into this contract.
  • Calculate Loan-to-Value (LTV) Ratio: This ratio determines how much a user can borrow based on the collateral they provide. For instance, if the LTV ratio is 75%, a borrower needs to deposit $1,000 in ETH to borrow $750 worth of ERC20 tokens.
  • Monitor Collateral Value: The contract continuously monitors the market value of the collateral to ensure it stays above the required threshold. If it drops below the limit, liquidation can occur.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract CollateralManager {
    IERC20 public collateralToken;
    LendingPool public lendingPool;

    mapping(address => uint256) public collateralDeposited;
    uint256 public collateralizationRatio = 150;  // 150% over-collateralization

    constructor(address _collateralToken, address _lendingPool) {
        collateralToken = IERC20(_collateralToken);
        lendingPool = LendingPool(_lendingPool);
    }

    // Borrower deposits collateral
    function depositCollateral(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than zero");
        collateralToken.transferFrom(msg.sender, address(this), _amount);
        collateralDeposited[msg.sender] += _amount;
    }

    // Borrower can borrow against their collateral
    function borrowAgainstCollateral(uint256 _borrowAmount) external {
        uint256 maxBorrow = (collateralDeposited[msg.sender] * 100) / collateralizationRatio;
        require(_borrowAmount <= maxBorrow, "Borrow amount exceeds collateral limit");
        
        lendingPool.borrow(_borrowAmount);
    }

    // Borrower repays the loan and retrieves their collateral
    function repayLoan(uint256 _amount) external {
        lendingPool.repay(_amount);
        
        // After repaying, the borrower can withdraw their collateral
        uint256 collateralToReturn = collateralDeposited[msg.sender];
        collateralDeposited[msg.sender] = 0;
        collateralToken.transfer(msg.sender, collateralToReturn);
    }
}

This contract acts as insurance for lenders, guaranteeing that they can recover their funds even if the borrower defaults or the collateral value decreases.

4. Liquidation Contract

This contract steps in when the value of the borrower’s collateral falls below the required threshold due to market volatility. It ensures that lenders are protected by liquidating the collateral to cover the outstanding loan.

Key Functions:

  • Trigger Liquidation: When the collateral-to-loan ratio drops below a safe margin, the contract can automatically trigger a liquidation process.
  • Sell Collateral: The collateral is sold off in the open market (usually through decentralized exchanges) to repay the loan and interest.
  • Repay Lenders: The proceeds from the sale are used to repay the lenders, ensuring they don’t lose their funds.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./CollateralManager.sol";

contract Liquidation {
    CollateralManager public collateralManager;

    constructor(address _collateralManager) {
        collateralManager = CollateralManager(_collateralManager);
    }

    // Function to liquidate collateral if the value drops below the threshold
    function liquidateCollateral(address borrower) external {
        uint256 collateral = collateralManager.collateralDeposited(borrower);
        require(collateral > 0, "No collateral to liquidate");

        uint256 borrowedAmount = collateralManager.lendingPool().borrowedAmounts(borrower);
        uint256 maxBorrow = (collateral * 100) / collateralManager.collateralizationRatio();

        // If collateral drops below the safety ratio, liquidate
        if (borrowedAmount > maxBorrow) {
            // Liquidate collateral and repay the loan
            collateralManager.lendingPool().repay(borrowedAmount);
            // Transfer collateral to liquidators (or back to lending pool)
            collateralManager.collateralToken().transfer(address(collateralManager.lendingPool()), collateral);
        }
    }
}

The Liquidation Contract ensures the platform remains solvent, even in volatile markets, by enforcing proper liquidation mechanisms.

5. Interest Rate Model Contract

This smart contract calculates the interest rates for loans and deposits, typically using an algorithmic model based on supply and demand.

Key Functions:

  • Calculate Borrowing Interest: The interest rate for borrowers is determined by the ratio of borrowed funds to total liquidity in the pool. When more liquidity is borrowed, the interest rate increases to encourage more lenders to deposit assets.
  • Calculate Lender Interest: The interest paid by the borrower is distributed to the lenders, and the contract calculates how much each lender earns based on their deposited assets.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract InterestRateModel {
    uint256 public baseRate = 5;  // 5% annual base rate
    uint256 public utilizationRate;

    function calculateInterest(uint256 totalBorrowed, uint256 totalLiquidity) external returns (uint256) {
        utilizationRate = (totalBorrowed * 100) / (totalBorrowed + totalLiquidity);
        return baseRate + utilizationRate;  // Simple model, more complex models can be used
    }
}

This Interest Rate Model Contract maintains the equilibrium of the lending platform by adjusting interest rates dynamically, ensuring both borrowers and lenders are incentivized.

6. Governance Contract (Optional)

Some decentralized lending platforms, like Aave or Compound, introduce governance mechanisms that allow users (often token holders) to vote on platform changes. This might include:

  • Adjusting interest rate models
  • Setting collateralization thresholds
  • Deciding on which tokens to support

Key Functions:

  • Voting: Token holders can propose and vote on protocol changes.
  • Execution: Once a proposal passes a vote, the contract automatically implements the changes in the platform.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Governance {
    struct Proposal {
        string description;  // Description of the proposal
        uint256 votesFor;    // Votes in favor
        uint256 votesAgainst;// Votes against
        uint256 deadline;    // Voting deadline
        bool executed;       // Whether the proposal has been executed
    }

    address public owner;
    uint256 public proposalCount = 0;
    uint256 public votingDuration = 3 days;
    
    mapping(uint256 => Proposal) public proposals;
    mapping(address => uint256) public votingPower;  // Voting power of each address
    mapping(uint256 => mapping(address => bool)) public hasVoted; // To track who voted on which proposal

    event ProposalCreated(uint256 indexed proposalId, string description, uint256 deadline);
    event VoteCasted(uint256 indexed proposalId, address voter, bool support, uint256 votes);
    event ProposalExecuted(uint256 indexed proposalId);

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can perform this action");
        _;
    }

    // Only token holders with voting power can vote, so we assume tokens are locked for governance
    function setVotingPower(address _voter, uint256 _power) external onlyOwner {
        votingPower[_voter] = _power;
    }

    // Create a new proposal
    function createProposal(string memory _description) external onlyOwner {
        proposalCount++;
        proposals[proposalCount] = Proposal({
            description: _description,
            votesFor: 0,
            votesAgainst: 0,
            deadline: block.timestamp + votingDuration,
            executed: false
        });

        emit ProposalCreated(proposalCount, _description, block.timestamp + votingDuration);
    }

    // Vote on a proposal
    function vote(uint256 _proposalId, bool _support) external {
        require(votingPower[msg.sender] > 0, "No voting power");
        require(block.timestamp < proposals[_proposalId].deadline, "Voting period has ended");
        require(!hasVoted[_proposalId][msg.sender], "Already voted");

        if (_support) {
            proposals[_proposalId].votesFor += votingPower[msg.sender];
        } else {
            proposals[_proposalId].votesAgainst += votingPower[msg.sender];
        }

        hasVoted[_proposalId][msg.sender] = true;

        emit VoteCasted(_proposalId, msg.sender, _support, votingPower[msg.sender]);
    }

    // Execute the proposal if voting period has ended and majority supports it
    function executeProposal(uint256 _proposalId) external onlyOwner {
        Proposal storage proposal = proposals[_proposalId];
        require(block.timestamp >= proposal.deadline, "Voting period not yet over");
        require(!proposal.executed, "Proposal already executed");

        // Simple majority rule for now
        require(proposal.votesFor > proposal.votesAgainst, "Proposal did not pass");

        proposal.executed = true;

        // Here, we would implement the logic for executing the proposal's decision.
        // For example, adjusting interest rates, changing collateralization ratios, etc.
        
        emit ProposalExecuted(_proposalId);
    }

    // Owner can change the voting duration if needed
    function setVotingDuration(uint256 _duration) external onlyOwner {
        votingDuration = _duration;
    }
}

How These Smart Contracts Work Together

  1. Lender deposits tokens: A lender deposits their ERC20 tokens into the Lending Pool Contract. This contract records the amount and issues interest-bearing tokens.
  2. Borrower locks collateral: To request a loan, a borrower deposits collateral into the Collateral Management Contract. The system checks that the collateral value meets the required LTV ratio.
  3. Loan issuance: If the collateral is sufficient, the Lending Pool Contract transfers the borrowed amount to the borrower.
  4. Interest accrual: The Interest Rate Model Contract calculates the interest due from the borrower, and lenders earn interest over time.
  5. Monitoring and liquidation: The Collateral Management Contract monitors the value of the collateral. If the value drops, the Liquidation Contract triggers, selling off the collateral to repay the loan.
  6. Repayment: If the borrower repays the loan (plus interest), their collateral is returned by the Collateral Management Contract, and the lender receives the interest and principal.

Conclusion

Implementing a decentralized lending platform using ERC20 tokens is a powerful example of how blockchain technology can transform the financial landscape. By leveraging smart contracts, we can create a trustless, transparent, and efficient system where users can lend and borrow assets without intermediaries. Throughout this journey, we’ve explored key components such as lending pools, collateral management, liquidation mechanisms, and governance structures that ensure the platform remains secure, stable, and adaptable.

The beauty of decentralized finance (DeFi) lies in its flexibility and community-driven nature. With governance contracts in place, platforms like these can evolve continuously based on the collective decisions of token holders. As more assets and users join the DeFi ecosystem, the opportunities to innovate and enhance decentralized lending will only grow, leading to a more inclusive financial system that empowers individuals around the world.

By building such a platform, you are not just creating a new service—you are contributing to the financial future.

Leave A Comment