当前位置:   article > 正文

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

区块链锁仓

目录

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 继承接口

  1. interface IBEP20 {
  2. function totalSupply() external view returns (uint256);
  3. function balanceOf(address account) external view returns (uint256);
  4. function transfer(address recipient, uint256 amount) external returns (bool);
  5. function allowance(address owner, address spender) external view returns (uint256);
  6. function approve(address spender, uint256 amount) external returns (bool);
  7. function transferFrom(
  8. address sender,
  9. address recipient,
  10. uint256 amount
  11. ) external returns (bool);
  12. event Transfer(address indexed from, address indexed to, uint256 value);
  13. event Approval(address indexed owner, address indexed spender, uint256 value);
  14. }
  • 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 变量定义及构造函数

  1. IBEP20 private tokenContract;
  2. struct LockInfo {
  3. uint256 amount;
  4. uint createTimestamp;
  5. uint256 unlockTimestamp;
  6. address owner;
  7. uint256 lockNo;
  8. }
  9. mapping(address=>LockInfo) private lockerBalance;
  10. event TokenLocked(address indexed account, uint256 amount, uint256 lockDuration);
  11. event TokenUnLocked(address indexed account, uint256 amount);
  12. uint256 private lockerPool=0;
  13. address[] private lockerAddresses;
  14. LockInfo[] private lockerHistoryList;
  15. 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  锁定

  1. function lockerToken(
  2. uint256 _amount,
  3. uint256 _lockDuration
  4. ) public {
  5. require(_lockDuration>0,"the lockDuration must be more than 0");
  6. require(!checkAddressLocked(msg.sender),"this address has locked,pls unlock");
  7. uint256 lockAmount=_amount*10**_decimals;
  8. require(tokenContract.balanceOf(msg.sender)>=lockAmount,"Token less amount");
  9. require(tokenContract.allowance(msg.sender,address(this))>=lockAmount, "Token allowance not");
  10. require(tokenContract.transferFrom(msg.sender,address(this),lockAmount), "Token transfer failed");
  11. uint256 unlockTimestamp = block.timestamp + _lockDuration;
  12. lockerBalance[msg.sender] = LockInfo({
  13. amount: lockAmount,
  14. createTimestamp: block.timestamp,
  15. unlockTimestamp: unlockTimestamp,
  16. owner:msg.sender,
  17. lockNo:lockerAddresses.length
  18. });
  19. lockerPool+=lockAmount;
  20. lockerAddresses.push(msg.sender);
  21. emit TokenLocked(msg.sender, _amount, _lockDuration);
  22. }

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

3.3.2  解锁

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

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

3.3.3  获取锁仓列表

  1. function getLockerList() public view returns(LockInfo[] memory) {
  2. uint256 length = lockerAddresses.length;
  3. LockInfo[] memory lockInfos = new LockInfo[](length);
  4. for (uint256 i = 0; i < length; i++) {
  5. address addr = lockerAddresses[i];
  6. lockInfos[i] = lockerBalance[addr];
  7. }
  8. return lockInfos;
  9. }

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

3.3.4 其它方法

  1. function checkLockTimeExpired(address addr) public view returns (bool){
  2. if (checkAddressLocked(addr)){
  3. if(lockerBalance[addr].unlockTimestamp<=block.timestamp){
  4. return true;
  5. }
  6. }
  7. return false;
  8. }
  9. function getLockerHistoryList() public view returns(LockInfo[] memory){
  10. return lockerHistoryList;
  11. }
  12. function getUserLocker() public view returns(LockInfo memory){
  13. return lockerBalance[msg.sender];
  14. }
  15. function getLockerSize() public view returns(uint256){
  16. return lockerAddresses.length;
  17. }
  18. function getLockPool() public view returns(uint256){
  19. return lockerPool;
  20. }
  21. function getTimestamp() public view returns(uint256){
  22. return block.timestamp;
  23. }

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

4.部署及功能测试


4.1 合约部署地址

BabyBonkLocker | Address 0x285387e8286e351047464750127142ed322a0a7f | BscScan


4.2 测试请求记录截图

4.3 测试视频​​​​​​​

这边后面会开放界面代码,请关注更新!!!!

录屏_选择区域_20240225093939

5.合约代码

  1. /**
  2. *Submitted for verification at BscScan.com on 2024-02-01
  3. */
  4. // SPDX-License-Identifier: MIT
  5. pragma solidity ^0.8.22;
  6. interface IBEP20 {
  7. function totalSupply() external view returns (uint256);
  8. function balanceOf(address account) external view returns (uint256);
  9. function transfer(address recipient, uint256 amount) external returns (bool);
  10. function allowance(address owner, address spender) external view returns (uint256);
  11. function approve(address spender, uint256 amount) external returns (bool);
  12. function transferFrom(
  13. address sender,
  14. address recipient,
  15. uint256 amount
  16. ) external returns (bool);
  17. event Transfer(address indexed from, address indexed to, uint256 value);
  18. event Approval(address indexed owner, address indexed spender, uint256 value);
  19. }
  20. abstract contract Context {
  21. function _msgSender() internal view virtual returns (address) {
  22. return msg.sender;
  23. }
  24. function _msgData() internal view virtual returns (bytes calldata) {
  25. this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
  26. return msg.data;
  27. }
  28. }
  29. abstract contract Ownable is Context {
  30. address private _owner;
  31. event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  32. constructor() {
  33. _setOwner(_msgSender());
  34. }
  35. function owner() public view virtual returns (address) {
  36. return _owner;
  37. }
  38. modifier onlyOwner() {
  39. require(owner() == _msgSender(), "Ownable: caller is not the owner");
  40. _;
  41. }
  42. function renounceOwnership() public virtual onlyOwner {
  43. _setOwner(address(0));
  44. }
  45. function transferOwnership(address newOwner) public virtual onlyOwner {
  46. require(newOwner != address(0), "Ownable: new owner is the zero address");
  47. _setOwner(newOwner);
  48. }
  49. function _setOwner(address newOwner) private {
  50. address oldOwner = _owner;
  51. _owner = newOwner;
  52. emit OwnershipTransferred(oldOwner, newOwner);
  53. }
  54. }
  55. contract BabyBonkLocker is Context, Ownable{
  56. IBEP20 private tokenContract;
  57. struct LockInfo {
  58. uint256 amount;
  59. uint createTimestamp;
  60. uint256 unlockTimestamp;
  61. address owner;
  62. uint256 lockNo;
  63. }
  64. mapping(address=>LockInfo) private lockerBalance;
  65. event TokenLocked(address indexed account, uint256 amount, uint256 lockDuration);
  66. event TokenUnLocked(address indexed account, uint256 amount);
  67. uint256 private lockerPool=0;
  68. address[] private lockerAddresses;
  69. LockInfo[] private lockerHistoryList;
  70. uint8 constant _decimals = 9;
  71. constructor(
  72. address payable _token
  73. ){
  74. tokenContract=IBEP20(_token);
  75. }
  76. function lockerToken(
  77. uint256 _amount,
  78. uint256 _lockDuration
  79. ) public {
  80. require(_lockDuration>0,"the lockDuration must be more than 0");
  81. require(!checkAddressLocked(msg.sender),"this address has locked,pls unlock");
  82. uint256 lockAmount=_amount*10**_decimals;
  83. require(tokenContract.balanceOf(msg.sender)>=lockAmount,"Token less amount");
  84. require(tokenContract.allowance(msg.sender,address(this))>=lockAmount, "Token allowance not");
  85. require(tokenContract.transferFrom(msg.sender,address(this),lockAmount), "Token transfer failed");
  86. uint256 unlockTimestamp = block.timestamp + _lockDuration;
  87. lockerBalance[msg.sender] = LockInfo({
  88. amount: lockAmount,
  89. createTimestamp: block.timestamp,
  90. unlockTimestamp: unlockTimestamp,
  91. owner:msg.sender,
  92. lockNo:lockerAddresses.length
  93. });
  94. lockerPool+=lockAmount;
  95. lockerAddresses.push(msg.sender);
  96. emit TokenLocked(msg.sender, _amount, _lockDuration);
  97. }
  98. function unLockerToken() public {
  99. LockInfo memory lockInfo = lockerBalance[msg.sender];
  100. require(lockInfo.unlockTimestamp <= block.timestamp, "Tokens still locked");
  101. require(tokenContract.approve(address(this),lockInfo.amount),"unlock approve failed");
  102. require(tokenContract.transfer(msg.sender, lockInfo.amount), "Token transfer failed");
  103. require(tokenContract.approve(address(this),0),"unlock approve 0 failed");
  104. lockerHistoryList.push(lockInfo);
  105. lockerPool-=lockInfo.amount;
  106. lockerAddresses[lockInfo.lockNo]=lockerAddresses[lockerAddresses.length-1];
  107. lockerAddresses.pop();
  108. delete lockerBalance[msg.sender];
  109. emit TokenUnLocked(msg.sender, lockInfo.amount);
  110. }
  111. function checkAddressLocked(address addr) public view returns (bool) {
  112. return lockerBalance[addr].unlockTimestamp !=0;
  113. }
  114. function getLockerList() public view returns(LockInfo[] memory) {
  115. uint256 length = lockerAddresses.length;
  116. LockInfo[] memory lockInfos = new LockInfo[](length);
  117. for (uint256 i = 0; i < length; i++) {
  118. address addr = lockerAddresses[i];
  119. lockInfos[i] = lockerBalance[addr];
  120. }
  121. return lockInfos;
  122. }
  123. function checkLockTimeExpired(address addr) public view returns (bool){
  124. if (checkAddressLocked(addr)){
  125. if(lockerBalance[addr].unlockTimestamp<=block.timestamp){
  126. return true;
  127. }
  128. }
  129. return false;
  130. }
  131. function getLockerHistoryList() public view returns(LockInfo[] memory){
  132. return lockerHistoryList;
  133. }
  134. function getUserLocker() public view returns(LockInfo memory){
  135. return lockerBalance[msg.sender];
  136. }
  137. function getLockerSize() public view returns(uint256){
  138. return lockerAddresses.length;
  139. }
  140. function getLockPool() public view returns(uint256){
  141. return lockerPool;
  142. }
  143. function getTimestamp() public view returns(uint256){
  144. return block.timestamp;
  145. }
  146. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/651213
推荐阅读
相关标签
  

闽ICP备14008679号