# 非 ERC20 代币流动性证明集成

### 介绍 <a href="#introduction" id="introduction"></a>

用户通常将ERC20凭证代币存入[奖励金库](/berachain-docs/learn/proof-of-liquidity/rewardvaults.md)来参与流动性证明，以赚取`$BGT` 。然而，这种方法并不适用于所有协议。

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

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

{% hint style="danger" %}
请注意，本节仅提供一种将PoL与非ERC20协议集成的可行性解决方案。该解决方案并不详尽，不是所有用例都适用。
{% endhint %}

### 方案描述 <a href="#description-of-approach" id="description-of-approach"></a>

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

用户质押虚拟代币，赚取`$BGT`，如同在PoL金库中质押了ERC20凭证代币。此方案由[奖励金库](/berachain-docs/learn/proof-of-liquidity/rewardvaults.md)合约中的`delegateStake`和`delegateWithdraw`实现。

### 先决条件

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

* 参考[Foundry安装文档](https://book.getfoundry.sh/getting-started/installation)安装该软件。

### Forge设置

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

```bash
forge init pol-smart-stake --no-commit --no-git;
cd pol-smart-stake;
forge install OpenZeppelin/openzeppelin-contracts --no-commit --no-git;
```

2. 创建`remappings.txt`文件，用于导入OpenZeppelin：

```bash
# FROM: ./pol-smart-stake

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

### 合约执行

1. **在**`src/StakingToken.sol`**中部署虚拟代币合约：**

```solidity
// 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`可以铸造和销毁代币。

2. **在**`src/ProtocolContract.sol`**中部署虚拟协议合约：**

```solidity
// 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`，并与相关的奖励金库交互，以体现用户的质押/解除质押操作。

3. **在**`src/interfaces/IRewardVaults.sol`**中添加PoL接口：**

```solidity
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`的测试套件示例。

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

```solidity

// 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);
    }
}
```

### 运行测试 <a href="#run-the-test" id="run-the-test"></a>

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

```bash
# 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)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.docszh.com/berachain-docs/developers/developer-guides/advanced-pol.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
