为了账号安全,请及时绑定邮箱和手机立即绑定

如何一步步实践编写动态质押智能合约

简介

在不断扩大的去中心化金融(DeFi)领域中,staking已成为用户通过参与网络安全和治理赚取奖励的一种流行机制。与传统的金融体系不同,staking允许加密货币持有者锁定其资产以支持区块链操作,同时赚取被动收入。然而,传统的staking模式通常存在一些限制,例如锁定期和固定奖励结构,这样的限制可能会影响用户的灵活性和参与度。

本文提供了一份详尽的操作指南,介绍如何在以太坊上创建动态质押智能合约。我们将逐步构建一个生产就绪的质押系统,该系统支持实时奖励计算和灵活提取。
本文是动态质押的基本理解的实际操作。
通过本逐步教程,你将学会:

  • 创建一个可升级的质押合约,具有基于角色的访问控制
  • 实现基于股份的奖励计算,以实现精准的代币分配
  • 构建灵活的存款和取款机制,不设锁定期限
  • 添加必要的安全特性和管理控制
  • 实现实时奖励跟踪和分配

无论你是正在构建一个 DeFi 协议还是增强现有的平台,这本实用指南将为你提供所需的 技术知识,以实现一个稳健的动态质押系统。这里的代码示例和实现模式经过实战考验,可以直接投入生产使用,帮助你创建真正符合现代 DeFi 用户需求的质押机制。

让我们深入技术实现的过程,了解每个组件是如何相互配合工作的,从而创建一个灵活而安全的动态质押系统。

实现的具体细节

让我们来分解动态质押合同的关键部分:

1. 合同的结构与继承

    合约 Staking 是
        Initializable,
        UUPSUpgradeable,
        AccessControlUpgradeable,
        PausableUpgradeable
    {
        using EnumerableSet for EnumerableSet.AddressSet;

切换到全屏模式,退出全屏

合同基于:

  • 初始化:用于可升级合约的初始化
  • UUPSUpgradeable:用于升级功能
  • AccessControlUpgradeable:用于角色基础的访问控制
  • PausableUpgradeable:紧急暂停:用于应急功能

2. 状态变量和数据结构

    struct Stake {
        // 记录质押的MRKST数量和所占股份
        uint256 stakedMRKST;
        uint256 shares;
    }

    // MRKST是IERC20合约的实例
    IERC20 private MRKST;
    // stakeholders存储所有参与者的地址集合
    EnumerableSet.AddressSet private stakeholders;
    // totalStakes记录所有质押的MRKST总数
    uint256 private totalStakes;
    // totalShares记录所有质押的股份总数
    uint256 private totalShares;
    // stakeholderToStake映射参与者地址到其质押信息
    mapping(address => Stake) private stakeholderToStake;

全屏模式,退出全屏

3. 主要功能

初始化过程

function initialize(
    address admin1,
    address admin2,
    address _MRKST
) public initializer {
    AccessControlUpgradeable.__AccessControl_init();
    PausableUpgradeable.__Pausable_init();
    ADMIN_ROLE = keccak256("管理员角色");
    _setupRole(ADMIN_ROLE, admin1);
    _setupRole(ADMIN_ROLE, admin2);
    MRKST = IERC20(_MRKST);
    base = 10**18;
}

进入全屏 退出全屏

这个功能用于初始化合约。

  • 初始化合约状态
  • 设置两位管理员以增强安全性
  • 连接质押代币合约
  • 设置基本单位(10^18)以实现精确计算
  • 设置访问控制和暂停功能

质押实施

    function createStake(uint256 stakeAmount) public whenNotPaused isInitialRatioSet {
        uint256 shares = (stakeAmount * totalShares) / MRKST.balanceOf(address(this));

        require(MRKST.transferFrom(msg.sender, address(this), stakeAmount), "MRKST transfer failed");

        stakeholders.add(msg.sender);
        stakeholderToStake[msg.sender].stakedMRKST += stakeAmount;
        stakeholderToStake[msg.sender].shares += shares;
        totalStakes += stakeAmount;
        totalShares += shares;
    }

进入全屏,退出全屏

这功能

  • 接受用户存款
  • 根据当前的代币与股份比率计算股份
  • 将用户的代币转移到合约
  • 记录质押详情到存储
  • 更新全局总额
  • 将用户加入到股东列表中

撤回

function 撤回质押(uint256 stakeAmount) public whenNotPaused {
    uint256 stakeholderStake = stakeholderToStake[msg.sender].stakedMRKST;
    uint256 stakeholderShares = stakeholderToStake[msg.sender].shares;

    require(stakeholderStake >= stakeAmount, "质押金额不足!");

    uint256 sharesToWithdraw = (stakeAmount * stakeholderShares) / stakeholderStake;
    uint256 rewards = 计算奖励(stakeAmount, stakeholderStake, stakeholderShares);

    更新质押和份额(msg.sender, stakeAmount, sharesToWithdraw);
    转移奖励(msg.sender, stakeAmount, rewards);
}

点击这里进入全屏模式,点击这里退出全屏模式

这个功能

  • 处理提现请求
  • 计算已赚取的奖励
  • 确定要销毁的股份份额
  • 更新用户的股份余额
  • 转移本金及奖励
  • 如果用户完全提现,则将用户从股东名单中移除

奖励计算

定义一个名为 rewardOf 的函数,该函数接收一个地址参数 stakeholder,并返回一个 uint256 类型的数据。stakeholderToStake 映射中存储了各个参与者(stakeholder)的 stakedMRKSTshares,其中 stakedMRKST 表示参与者已经质押的 MRKST 数量,shares 则表示该参与者持有的股份。函数首先获取参与者对应的质押的 MRKST 数量和持有的股份。如果参与者持有的股份为0,则直接返回0。

接下来,函数计算参与者质押比例 stakedRatio,公式为 (stakeholderStake * base) / stakeholderShares。然后计算当前的质押比例 currentRatio,公式为 (MRKST.balanceOf(address(this)) * base) / totalShares。如果当前的质押比例小于或等于参与者质押比例,则直接返回0。

接下来,计算奖励 rewards,公式为 (stakeholderShares * (currentRatio - stakedRatio)) / base。最后返回计算得到的奖励 rewards

进入全屏 退出全屏

这个作用

  • 计算持有人的总待领奖励
  • 比较初始质押量与当前质押价值的比例
  • 考虑所有累积奖励
  • 返回可领取的总奖励

奖励发放:

function rewardForStake(address stakeholder, uint256 stakeAmount)
    public
    view
    returns (uint256)
  {
    uint256 stakeholderStake = stakeholderToStake[stakeholder].质押MRKST数量;
    uint256 stakeholderShares = stakeholderToStake[stakeholder].shares;

    require(stakeholderStake >= stakeAmount, "您的质押数量不足,请检查您的质押数量!");

    uint256 stakedRatio = (stakeholderStake * base) / stakeholderShares;
    uint256 currentRatio = (MRKST.balanceOf(address(this)) * base) / totalShares;
    uint256 sharesToWithdraw = (stakeAmount * stakeholderShares) / stakeholderStake;

    if (currentRatio <= stakedRatio) {
      // 如果当前比例小于或等于质押比,则返回0
      return 0;
    }

    uint256 rewards = (sharesToWithdraw * (currentRatio - stakedRatio)) / base;
    // 返回奖励值
    return rewards;
  }

全屏模式:进入 全屏模式:退出

这个功能

  • 计算部分提取的奖励金额
  • 确定奖励的相应比例
  • 验证提取金额
  • 返回请求提取的具体奖励

退款锁定功能

      function refundLockedStake(uint256 from, uint256 to) public, hasAdminRole {
        require(to <= stakeholders.length(), "无效的 `to` 参数值");
        uint256 s;

        for (s = from; s < to; s += 1) {
          totalStakes -= stakeholderToStake[stakeholders.at(s)].stakedMRKST;

          require(
            MRKST.transfer(
              stakeholders.at(s),
              stakeholderToStake[stakeholders.at(s)].stakedMRKST
            ),
            "无法转移 MRKST"
          );

          将 stakeholders.at(s) 的锁定 MRKST 设为 0;
        }
      }

切换到全屏模式,退出全屏

这功能

  • 紧急撤回机制
  • 分批退款处理
  • 退还原始押注金额
  • 清除押注记录信息
最后结论

这种动态质押系统提供了一种既灵活又安全的方式来管理代币质押,同时进行实时奖励计算。这种基于份额的系统确保了奖励分配的公平性,同时保证计算的准确性。

未来可能的种种
  • 支持多种代币、分级奖励系统、治理整合、更复杂奖励策略、更强大的分析和报告

这一实现方案为在 DeFi 应用中构建更复杂的质押方案奠定了坚实的基础。

如需更多详细信息,可参阅源代码

谢谢!祝你写代码开心!😍

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消