基于ERC20代币协议实现的去中心化应用平台

news2025/1/20 3:42:58

文章目录

  • 内容简介
  • 设计逻辑
  • ERC20TokenLoanPlatform 合约
    • 事件
    • 结构体
    • 状态变量
    • 函数
  • Remix 运行实现
    • 部署相关智能合约
    • 存款和取款
    • 贷款和还款
  • 源码地址

内容简介

使用 solidity 实现的基于 ERC20 代币协议的借贷款去中心化应用平台(极简版)。实现存款、取款、贷款、还款以及利息计算的功能。

设计逻辑

  • 平台提供ERC20协议代币的相关存取和利息计算工作。部署智能合约时初始化贷款和存款的年利率、代币实现地址。
  • 用户可以将手中的代币存入平台,等到一定的期限再次拿出获得本金加利息。也可以向平台申请代币,在一定的期限之后自主还款即可。

ERC20TokenLoanPlatform 合约

事件

合约包含4个事件,包括 Deposit 存款、Withdrawal 取款、CreateLoan 贷款、PayLoan 还款。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "erc-token-standard/ERC/IERC20.sol"; 

// 基于ERC20代币协议的借贷款平台 
contract ERC20TokenLoanPlatform {
    event Deposit(address depositor, uint amount ,uint DepositTime); 
    event Withdrawal(address payee, uint amount, uint WithdrawalTime); 
    event CreateLoan(address loanAddress, uint amount, uint interest, uint CreateLoanTime); 
    event PayLoan(address loanAddress, uint amount, bool total, uint PayLoanTime); 

结构体

合约包含2个结构体, 包括 Client 客户信息、Loan 贷款信息。

	// 客户信息 
	struct Client {
	    uint amount;
	    uint depositTime; 
	    uint withdrawalTime;
	}
	
	// 贷款信息
	struct Loan {
	    address loanAddress; 
	    uint amount;
	    uint interest; 
	}

状态变量

本合约使用了6个状态变量,其中有 clients 客户信息映射、loans 贷款信息映射、annualInterestRate 贷款年利率、annualDepositRate 存款年利率、tokenAddress 代币实现地址、erc20Token 代币对象。

	mapping(address => Client) private clients; 
    mapping (uint => Loan) private loans;
    uint public annualInterestRate;        // 贷款年利率,如5%
    uint public annualDepositRate;         // 存款年利率,如2%
    address public tokenAddress;           // ERC20代币地址 
    IERC20 erc20Token; 

函数

本合约包含了8个基本函数,其中包括构造函数、deposit 存款、withdrawal 取款、createLoan 贷款、payLoan 还款、loanInquiry 待还款查询、depositInquiry 账户余额查询、balanceInquiry 客户信息查询 。

	// 构造函数
    constructor(address _tokenAddress, uint _initLoanInterest, uint _initDepositInterest) {
        tokenAddress = _tokenAddress;           // 初始化代币地址 
        erc20Token = IERC20(tokenAddress);      // 声明IERC20接口合约变量
        annualInterestRate = _initLoanInterest; 
        annualDepositRate = _initDepositInterest;   
    }

    // 存款 
    function deposit(uint _amount) public {
        require(_amount > 0, "Amount is less than or equal to 0.");      // 存款代币数不能为0     
        require(erc20Token.balanceOf(msg.sender) >= _amount, "Number of tokens is insufficient.");            // 检查持有代币数是否大于等于存款代币数
        require(erc20Token.allowances(msg.sender, address(this)) >= _amount, "Not enough approvals.");        // 检查是否有足够的授权 

        erc20Token.transferFrom(msg.sender, address(this), _amount);             
        clients[msg.sender].amount += _amount;                      // 增加存款
        if (clients[msg.sender].depositTime == 0){
            clients[msg.sender].depositTime = block.timestamp;      // 当前的存款时间 
        }
        
        emit Deposit(msg.sender,_amount ,block.timestamp); 
    }

    // 取款 
    function withdrawal(uint _amount) public {
        require(_amount > 0, "Amount is less than or equal to 0.");               // 取款代币数不能为0  
        uint timestamp = block.timestamp;   
        depositInquiry();                 // 计算本金 + 利息2% 
        require(clients[msg.sender].amount >= _amount, "Insufficient balance.");         // 检查存款代币数是否大于等于取款代币数 
        require(erc20Token.balanceOf(address(this)) >= _amount, "Platform bankruptcy."); // 检查平台的代币数是否满足取款 
         
        erc20Token.transfer(msg.sender, _amount);            
        clients[msg.sender].amount -= _amount; 
        clients[msg.sender].withdrawalTime = timestamp; 

        emit Withdrawal(msg.sender, _amount, timestamp);
    }

    // 贷款
    function createLoan(uint _amount) public returns (uint){
        require(_amount > 0, "Amount is less than or equal to 0.");            // 贷款不能小于0        
        require(erc20Token.balanceOf(address(this))*10/100 >= _amount, "Exceeding the platform loan limit.");        // 超过平台总存款的10%
        require(erc20Token.balanceOf(msg.sender) + clients[msg.sender].amount >= _amount*20/100, "Invalid loan.");   // 账户资产数需有贷款的20%

        erc20Token.transfer(msg.sender, _amount); 
        uint timestamp = block.timestamp;           // 以当前块的时间戳为贷款id值 
        uint interest = _amount * annualInterestRate / 100;     // 计算利率 
        
        loans[timestamp].loanAddress = msg.sender; 
        loans[timestamp].amount = _amount; 
        loans[timestamp].interest = interest;     // 计算利率 

        emit CreateLoan(msg.sender, _amount, interest, timestamp);

        return timestamp; 
    }

    // 还款
    function payLoan(uint _amount, uint _loanId) public {
        require(_amount > 0, "Amount is less than or equal to 0.");        // 还款需大于0
        require(erc20Token.balanceOf(msg.sender) >= _amount, "Number of tokens is insufficient.");            // 检查持有代币数是否大于等于存款代币数
        require(erc20Token.allowances(msg.sender, address(this)) >= _amount, "Not enough approvals.");        // 检查是否有足够的授权 

        uint total = loanInquiry(_loanId);        // 计算代还款 
        bool fullPayment = false; 

        // 还一部分或一次性还 
        if (total <= _amount) {     
            erc20Token.transferFrom(msg.sender, address(this), total);       // 一次性还完
            delete loans[_loanId];                                           // 删除贷款记录  
            fullPayment = true; 
        } else {    
            erc20Token.transferFrom(msg.sender, address(this), _amount);        // 还了一部分 _amount 
            loans[_loanId].amount -= _amount;                                   // 重写贷款记录 
        }
        
        emit PayLoan(msg.sender, _amount, fullPayment, block.timestamp);
    }

    
    // 待还款查询
    function loanInquiry(uint _loanId) public returns (uint) {
        require(loans[_loanId].amount != 0, "Invalid loan id.");
        require(loans[_loanId].loanAddress == msg.sender, "Non-personal enquiry.");

        // 计算需要还款的代币数: 本金 + 利息
        uint timestamp = block.timestamp; 
        uint diffDays = ( timestamp - _loanId) / 86400;                 // 时间戳转换为天数 
        uint total = loans[_loanId].amount + loans[_loanId].interest*diffDays/365;      // 总计还款数 
        
        loans[_loanId].amount = total;            //重写用户贷款数 

        return total; 
    }

    // 余额+利息的计算
    function depositInquiry() public  returns (uint){
        uint principal = clients[msg.sender].amount;    // 本金
        uint diffDays = (block.timestamp - clients[msg.sender].depositTime) / 86400;                 // 时间戳转换为天数 
        uint interest = principal * annualDepositRate / 100 * diffDays / 365;
        uint total = principal + interest;
        clients[msg.sender].amount = total;

        return total;
    }

    // 客户信息查询 
    function balanceInquiry() public returns (address, uint, uint, uint){
        depositInquiry();                 // 计算本金 + 利息2% 
        uint balance = clients[msg.sender].amount;
        uint depositTime = clients[msg.sender].depositTime;
        uint withdrawalTime = clients[msg.sender].withdrawalTime;

        return (msg.sender, balance, depositTime, withdrawalTime);
    }
}

Remix 运行实现

部署相关智能合约

solidity实现ERC20代币标准。
先部署 ERC20 代币合约,初始化代币合约信息。
在这里插入图片描述
部署 ERC20TokenLoanPlatform 合约,初始化代币实现地址、贷款年利率5和存款年利率2。
在这里插入图片描述
首先需要给自己的账户铸造 200 个代币用于功能测试,
在这里插入图片描述
再给 ERC20TokenLoanPlatform 合约地址铸造一定数量的代币和授权代币转账权限,保证平台功能的正常运行。
在这里插入图片描述
在这里插入图片描述

存款和取款

调用 deposit 函数存款 100 个代币,
在这里插入图片描述
balanceInquiry 函数查看余额变化,这里已经显示自己的余额为刚才存入的100。
在这里插入图片描述
调用 withdrawal 函数取出指定数量的代币,不能超出自己的余额。平台会自动进行存款利息计算。
在这里插入图片描述
balanceInquiry 函数查看余额变化,这里已经显示自己的余额还剩下50。
在这里插入图片描述

贷款和还款

调用 createLoan 函数输入自己需要贷多少代币。在这里注意账户的资产需要有贷款数的20%才有资格贷款。成功后拿到此次贷款的id值 1703236890。
在这里插入图片描述
调用 payLoan 函数输入贷款的 id 值进行还款,可以先还一部分也可以一次性还完,平台会自动进行贷款利息计算。

在这里插入图片描述
调用 loanInquiry 函数输入贷款的 id 值进行待还款的查询。
在这里插入图片描述

源码地址

本文只是简单介绍,具体实现看代码。gitee 开源地址。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1330895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

低代码开发:数字化转型的引擎

引言 在当今数字化时代&#xff0c;组织面临着不断变化的市场需求和技术挑战。数字化转型已成为维持竞争力的关键&#xff0c;而低代码开发正在崭露头角&#xff0c;成为加速创新和数字化转型的有力工具。本文将深入探讨低代码开发的核心概念、优势和应用&#xff0c;以揭示它…

单调栈分类、封装和总结

作者推荐 map|动态规划|单调栈|LeetCode975:奇偶跳 通过枚举最小&#xff08;最大&#xff09;值不重复、不遗漏枚举所有子数组 C算法&#xff1a;美丽塔O(n)解法单调栈左右寻找第一个小于maxHeight[i]的left,right&#xff0c;[left,right]直接的高度都是maxHeight[i] 可以…

【动态规划算法(dp算法)】之背包问题

文章目录 背包问题动规五部曲一、0-1背包问题 &#xff1a;限制物品不可重复 (要么不选 要么选一个)二、完全背包问题&#xff1a;不限制重复&#xff08;要么不选 要么可以多选&#xff09;&#xff08;完全背包可以转化为0-1背包问题&#xff09; 动态规划&#xff1a;01背包…

宝塔面板 -- 创建第一个自己的网站

文章目录 前言 一、安装宝塔面板 二、注册宝塔面板 三、安装nginx 四、第一个hello world运行 五、总结 文章目录 前言一、安装宝塔面板二、注册宝塔面板三、安装nginx四、第一个hello world运行五、总结 前言 阿里云最近对在校大学生免费每人赠送一台服务器&#xff0c…

Apache ShenYu 网关JWT认证绕过漏洞 CVE-2021-37580

Apache ShenYu 网关JWT认证绕过漏洞 CVE-2021-37580 已亲自复现 漏洞名称漏洞描述影响版本 漏洞复现环境搭建漏洞利用 修复建议总结 Apache ShenYu 网关JWT认证绕过漏洞 CVE-2021-37580 已亲自复现) 漏洞名称 漏洞描述 Apache ShenYu是一个异步的&#xff0c;高性能的&#x…

第九周算法题(哈希映射,二分,Floyd算法 (含详细讲解) )

第九周算法题 第一题 题目来源&#xff1a;33. 搜索旋转排序数组 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a;整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 <…

HA启动Advanced SSH Web Terminal 提示附加组件似乎尚未准备就绪,它可能仍在启动。是否要再试一次?

环境&#xff1a; Home Assistant OS11.1 Advanced SSH & Web Terminal 17.0 问题描述&#xff1a; HA安装好SSH加载项&#xff0c;启动Advanced SSH & Web Terminal 提示附加组件似乎尚未准备就绪&#xff0c;它可能仍在启动。是否要再试一次&#xff1f; 解决方案…

尚硅谷 java 2023(基础语法)笔记

一、变量与运算符 1、HelloWorld的编写和执行 class HelloChina{public static void main(String[] args){System.out.println("hello,world!!你好&#xff0c;中国&#xff01;");} } 总结&#xff1a; 1. Java程序编写和执行的过程&#xff1a; 步骤1&#xff1…

解决 MATLAB 遗传算法中 exitflg=4 的问题

一、优化问题简介 以求解下述优化问题为例&#xff1a; P 1 : min ⁡ p ∑ k 1 K p k s . t . { ∑ k 1 K R k r e q l o g ( 1 α k ∗ p k ) ≤ B b s , ∀ k ∈ K p k ≥ 0 , ∀ k ∈ K \begin{align} {P_1:}&\mathop{\min}_{\bm{p}}{ \sum\limits_{k1}^K p_k } \no…

微软写了份GPT-4V说明书:166页讲解又全又详细demo示例一应俱全

原文&#xff1a;微软写了份GPT-4V说明书&#xff1a;166页讲解又全又详细demo示例一应俱全 - 哔哩哔哩 编者按&#xff1a;这篇文章深入研究了GPT-4V的用法、基本功能&#xff0c;用较大篇幅介绍了GPT-4V在遵循文字说明、视觉指向和视觉参考提示、视觉文本提示等方面展示出的…

Swiper轮播图系列

一、初始化Swiper new Swiper(.swiper-container, {initialSlide: 0,slidesPerView: 3,breakpoints: {750: {slidesPerView: 1},990: {slidesPerView: 2}},spaceBetween: 12,loop: true,speed: 1000,autoplay: {disableOnInteraction: false, // 手动滑动后&#xff0c;不停止…

阿里云 ACK One 新特性:多集群网关,帮您快速构建同城容灾系统

云布道师 近日&#xff0c;阿里云分布式云容器平台 ACK One[1]发布“多集群网关”[2]&#xff08;ACK One Multi-cluster Gateways&#xff09;新特性&#xff0c;这是 ACK One 面向多云、多集群场景提供的云原生网关&#xff0c;用于对多集群南北向流量进行统一管理。 基于 …

【UML】第9篇 类图(概念、作用和抽象类)(1/3)

目录 一、类图的概念 二、类图的主要作用 三、类图的构成 3.1 类的名称 3.2 抽象类&#xff08;Abstract Class&#xff09; 一、类图的概念 类图是UML模型中静态视图。它用来描述系统中的有意义的概念&#xff0c;包括具体的概念、抽象的概念、实现方面的概念等。静态视…

Pytorch项目,肺癌检测项目之四

# 安装图像处理 的两个包 simpleITK 和 ipyvolume # 安装缓存相关的两个包 diskcache 和 cassandra-driver import gzip from diskcache import FanoutCache, Disk from cassandra.cqltypes import BytesType from diskcache import FanoutCache,Disk,core from diskcache…

浏览器原理篇—渲染优化

渲染优化 通常一个页面有三个阶段&#xff1a;加载阶段、交互阶段和关闭阶段 加载阶段&#xff0c;是指从发出请求到渲染出完整页面的过程&#xff0c;影响到这个阶段的主要因素有网络和 JavaScript 脚本。交互阶段&#xff0c;主要是从页面加载完成到用户交互的整合过程&…

【星海出品】Keepalived 使用基础案例 (二)

keepalived 使用 [rootmaster ~]# cat /etc/keepalived/keepalived.conf ! Configuration File for keepalivedglobal_defs { //全局配置notification_email { //定义报警收件人邮件地址acassenfirewall.locfailoverfirewall.locsysadminfirewall.loc}notification_…

单例模式实现

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;JavaEE &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 单例模式 1. 什么是单例模式2. 饿汉模式3.…

JoySSL诚招SSL证书代理

不久前&#xff0c;阿里云宣布了一个让人稍感唏嘘的消息——它们的一年期免费SSL证书服务将停步&#xff0c;转而提供三个月期限的证书。这一变化&#xff0c;无疑会使得网站开发的公司在维持用户信任和网站安全上多出心思。然而&#xff0c;免费的午餐并没有彻底消失&#xff…

Epson打印机连接wifi

环境 Epson L3153 打印机联通无线光猫 背景 最近家里的联通宽带不太稳定&#xff0c;经常断网。今天打了联通客服电话&#xff0c;师傅上门来&#xff0c;说可能是光猫用的时间太长了&#xff0c;换了一个新的联通光猫&#xff0c;问题解决。 wifi的名称是 CU_Y3ft 和 CU_Y3…

使用Docker一键部署Uptime Kuma,并将监控服务映射至公网访问

文章目录 **主要功能**一、前期准备本教程环境为&#xff1a;Centos7&#xff0c;可以跑Docker的系统都可以使用本教程安装。本教程使用Docker部署服务&#xff0c;如何安装Docker详见&#xff1a; 二、Docker部署Uptime Kuma三、实现公网查看网站监控四、使用固定公网地址访问…