非 ERC20 代币流动性证明集成

介绍

用户通常将ERC20凭证代币存入奖励金库来参与流动性证明,以赚取$BGT 。然而,这种方法并不适用于所有协议。

本节将演示如何为无法自动生成质押凭证的ERC20代币,或需要实时跟踪余额的协议集成流动性证明 (PoL) 系统。例如,永续合约交易所可能希望奖励$BGT开仓用户,并在平仓时停止奖励。

通过采用本节方案,上述协议仍可参与PoL系统,同等享有PoL提供的高效率激励。

请注意,本节仅提供一种将PoL与非ERC20协议集成的可行性解决方案。该解决方案并不详尽,不是所有用例都适用。

方案描述

方案涉及创建一个虚拟的StakingToken,虚拟代币由协议代表用户质押在PoL金库中。虚拟代币用于跟踪用户的质押余额,在用户供应或提取流动性时,由协议铸造或销毁 (通过ProtocolContract实现)。

用户质押虚拟代币,赚取$BGT,如同在PoL金库中质押了ERC20凭证代币。此方案由奖励金库合约中的delegateStakedelegateWithdraw实现。

先决条件

开始之前,请确保你的本地设备上满足以下条件:

Forge设置

  1. 初始化Forge并安装依赖项:

forge init pol-smart-stake --no-commit --no-git;
cd pol-smart-stake;
forge install OpenZeppelin/openzeppelin-contracts --no-commit --no-git;
  1. 创建remappings.txt文件,用于导入OpenZeppelin:

# FROM: ./pol-smart-stake

echo "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/" > remappings.txt;

合约执行

  1. src/StakingToken.sol中部署虚拟代币合约:

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract StakingToken is ERC20, Ownable {
    constructor() ERC20("StakingToken", "STK") Ownable(msg.sender) {}

    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }

    function burn(address from, uint256 amount) external onlyOwner {
        _burn(from, amount);
    }
}

该合约创建了一个虚拟的ERC20代币,用于质押在PoL金库中,只有代币所有者ProtocolContract可以铸造和销毁代币。

  1. src/ProtocolContract.sol中部署虚拟协议合约:

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

import "./StakingToken.sol";
import {IBerachainRewardsVault, IBerachainRewardsVaultFactory} from "./interfaces/IRewardVaults.sol";

contract ProtocolContract {
    StakingToken public stakingToken;
    IBerachainRewardsVault public rewardVault;

    mapping(address => uint256) public userActivity;

    constructor(address _vaultFactory) {
        // Create new staking token
        stakingToken = new StakingToken();

        // Create vault for newly created token
        address vaultAddress = IBerachainRewardsVaultFactory(_vaultFactory)
            .createRewardsVault(address(stakingToken));

        rewardVault = IBerachainRewardsVault(vaultAddress);
    }

    function addActivity(address user, uint256 amount) external {
        // Protocol actions/logic here
        userActivity[user] += amount;

        // Mint StakingTokens
        stakingToken.mint(address(this), amount);

        // Stake tokens in RewardVault on behalf of user
        stakingToken.approve(address(rewardVault), amount);
        rewardVault.delegateStake(user, amount);
    }

    function removeActivity(address user, uint256 amount) external {
        // Protocol actions/logic here
        require(userActivity[user] >= amount, "Insufficient user activity");
        userActivity[user] -= amount;

        // Withdraw tokens from the RewardVault
        rewardVault.delegateWithdraw(user, amount);

        // Burn the withdrawn StakingTokens
        stakingToken.burn(address(this), amount);
    }
}

该合约是任意协议合约的简单用例:

  • userActivity表示特定于该协议的内部核算和运作逻辑。

  • addActivityremoveActivity余下功能是用于铸造和销毁虚拟代币StakingTokens,并与相关的奖励金库交互,以体现用户的质押/解除质押操作。

  1. src/interfaces/IRewardVaults.sol中添加PoL接口:

pragma solidity ^0.8.19;

interface IBerachainRewardsVault {
    function delegateStake(address account, uint256 amount) external;

    function delegateWithdraw(address account, uint256 amount) external;

    function getTotalDelegateStaked(
        address account
    ) external view returns (uint256);

    function balanceOf(address account) external returns (uint256);
}

interface IBerachainRewardsVaultFactory {
    function createRewardsVault(
        address stakingToken
    ) external returns (address);
}

这些接口定义了从Factory合约中启动新的奖励金库,以及与之交互的方法。

测试集成

现在,对所有部署进行整体测试,以确保上述集成操作符合预期。以下是ProtocolContract的测试套件示例。

请检查每项测试,以便更加了解如何应对并成功处理各种情况。


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

import "forge-std/Test.sol";
import "../src/ProtocolContract.sol";
import {IBerachainRewardsVault, IBerachainRewardsVaultFactory} from "../src/interfaces/IRewardVaults.sol";

contract ProtocolContractTest is Test {
    ProtocolContract public protocol;
    IBerachainRewardsVault public rewardVault;

    address public user1 = address(0x1);
    address public user2 = address(0x2);

    function setUp() public {
        IBerachainRewardsVaultFactory vaultFactory = IBerachainRewardsVaultFactory(
                0x2B6e40f65D82A0cB98795bC7587a71bfa49fBB2B
            );
        protocol = new ProtocolContract(address(vaultFactory));
        rewardVault = protocol.rewardVault();
    }

    function testAddActivity() public {
        protocol.addActivity(user1, 1);
        assertEq(protocol.userActivity(user1), 1);
        assertEq(rewardVault.balanceOf(user1), 1);
    }

    function testRemoveActivity() public {
        protocol.addActivity(user1, 2);
        protocol.removeActivity(user1, 1);
        assertEq(protocol.userActivity(user1), 1);
        assertEq(rewardVault.balanceOf(user1), 1);
    }

    function testMultipleUsers() public {
        protocol.addActivity(user1, 1);
        protocol.addActivity(user2, 2);
        assertEq(rewardVault.balanceOf(user1), 1);
        assertEq(rewardVault.balanceOf(user2), 2);
    }
}

运行测试

最后,运行测试程序,以检查上述集成是否按预期工作:

# FROM: ./pol-smart-stake

forge test --rpc-url https://bartio.rpc.berachain.com/;

# [Expected Output]:
# [⠊] Compiling...x
# No files changed, compilation skipped

# Ran 3 tests for test/StakingToken.t.sol:ProtocolContractTest
# [PASS] testAddActivity() (gas: 252067)
# [PASS] testMultipleUsers() (gas: 371503)
# [PASS] testRemoveActivity() (gas: 272693)
# Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 1.73s (1.22ms CPU time)

最后更新于