介绍
用户通常将ERC20凭证代币存入奖励金库来参与流动性证明,以赚取$BGT
。然而,这种方法并不适用于所有协议。
本节将演示如何为无法自动生成质押凭证的ERC20代币,或需要实时跟踪余额的协议集成流动性证明 (PoL) 系统。例如,永续合约交易所可能希望奖励$BGT
开仓用户,并在平仓时停止奖励。
通过采用本节方案,上述协议仍可参与PoL系统,同等享有PoL提供的高效率激励。
请注意,本节仅提供一种将PoL与非ERC20协议集成的可行性解决方案。该解决方案并不详尽,不是所有用例都适用。
方案描述
方案涉及创建一个虚拟的StakingToken
,虚拟代币由协议代表用户质押在PoL金库中。虚拟代币用于跟踪用户的质押余额,在用户供应或提取流动性时,由协议铸造或销毁 (通过ProtocolContract
实现)。
用户质押虚拟代币,赚取$BGT
,如同在PoL金库中质押了ERC20凭证代币。此方案由奖励金库合约中的delegateStake
和delegateWithdraw
实现。
先决条件
开始之前,请确保你的本地设备上满足以下条件:
Forge设置
forge init pol-smart-stake --no-commit --no-git;
cd pol-smart-stake;
forge install OpenZeppelin/openzeppelin-contracts --no-commit --no-git;
创建remappings.txt
文件,用于导入OpenZeppelin:
# FROM: ./pol-smart-stake
echo "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/" > remappings.txt;
合约执行
在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
可以铸造和销毁代币。
在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
表示特定于该协议的内部核算和运作逻辑。
addActivity
和removeActivity
余下功能是用于铸造和销毁虚拟代币StakingTokens
,并与相关的奖励金库交互,以体现用户的质押/解除质押操作。
在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)