目录
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 测试请求记录截图
4.3 测试视频
5.合约代码
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); }
- 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;
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); }
传入两个参数分别是锁定数量(_amount)和(_lockDuration)锁定时间。首先对锁定时间进行了检查,使锁定时间必须是>0的数,checkAddressLocked()检查用户是否被锁定,锁定的用户需先解锁才能再次锁定。对锁定数量进行精确度转换,使精确度符合链上精确度。对用户余额的检查,对用户授权的检查(锁定及用户需要当前合约转账,所以需要检查),然后使用transferFrom()请求者会向当前合约进行转移代币.unlockTimestamp为计算出来的过期时间(即当前时间+锁定时间),然后构建LockInfo结构体并放到lockerAddress中,锁仓池代币数量增加,将用户放入锁仓地址列表中。并发布代币锁定事件通知前端页面。
3.3.2 解锁
function unLockerToken() public { LockInfo memory lockInfo = lockerBalance[msg.sender]; require(lockInfo.unlockTimestamp 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