Page cover image

💻与 Alpaca Finance 集成

与 Alpaca Finance 集成

Alpaca Finance 允许 DeFi 应用程序和最终用户与其核心协议集成。一个简单的例子,就是开发人员可以构建一个自动复投协议,将用户的资金存入 Alpaca Finance 的存款池,并为用户复投收益以提高 gas 的使用效率和便利性。本文档的目的是帮助开发人员与我们的协议集成,以确保生态系统的整体安全。

重要数据

以下公共 API 包含我们前端使用的所有必要信息,因此您可以将它们用作自动与我们同步的来源。

AF1.0:

AF2.0:

API 中包含的信息示例:

  1. 所有贷款池及其统计数据,例如 APR、TVL、供应和借贷、ibToken 价格

  2. 所有 workers (农场)及其统计数据,例如 APR、TVL

  3. 所有自动化金库及其统计数据,例如 APY、自动化金库共享价格

智能合约集成

本节将介绍如何直接透过智能合约与 Alpaca Finance 集成。

贷款 (Lending)

Alpaca Finance 允许用户通过将加密资产存入我们的存款池,来赚取来自加密资产的利息收入。这些资产将会提供予来农民来开启杠杆仓位。任何与 EVM 兼容并部署在 BNB Chain 上的智能合约都可以与 Alpaca Finance 的借贷协议集成。

要将基础代币存入 Alpaca Finance 的存款池,首先应获取可用存款池及其相应的基础代币的列表。

  • 请参阅此处查看所有存款池合约地址的详细列表。

在向存款池存款之前,作为存款人的智能合约必须首先批准要存入的基础代币的支出限额。这将为Alpaca 的智能合约提供从发送方提取代币的能力。

// JavaScript
import { ERC20 } from '../typechain/ERC20'
import { MaxUint256 } from '@ethersproject/constants'

...

const yourSmartContractAddress = 'xxx'
tokenERC20.approve(yourSmartContractAddress, MaxUint256);

上面的 JavaScript 代码段落是关于如何批准代币的最大可能支出限额的简化流程。请注意,使用 MaxUint256 可能会带来安全风险,建议使用用户输入的确实代币数量。

存款是通过调用 deposit 方法并在 amountToken 参数中指定存款金额来进行的。 amountToken 的数据类型 (data type) 是 uint256 而该参数必须被格式化为存款代币的小数位 (例如,BNB使用 18 个小数位,1 BNB = 100000000000000000001*1E18)。存款代币如果是 BNB 的情况,存款池将为 BNB 包装成 wBNB,但交易必须包含足够的 BNB 作为原生代币发送。

// Solidity
address vaultContractAddress = '0xd7D069493685A581d27824Fc46EdA46B7EfC0063'; // BNB Vault
if (msg.value == 0) { // if no native token is sent, then it is a ERC20/BEP20 token deposit
	IERC20(tokenAddess).safeTransferFrom(address(msg.sender), address(this), amountToken);
}
// Allow transfer to vault
SafeToken.safeApprove(tokenAddess, vaultContractAddress, amountToken);
// Deposit to vaultDeposit to vault
IVault(vaultContractAddress).deposit(amountToken);

如果存款成功,存款池将铸造”计息代币” (interest-bearing tokens 即 ibTokens) 并将这些铸造的代币返还给调用者。

请注意,基于计息代币的设计,返回的计息代币数量将不等于提供的原生代币数量。deposit 方法的调用者必须正确记录用户在存款池中的存款份额,请阅读本文下方 “计息代币的计算” 部分的详细说明。

来自贷款的利息将计入 ibTokens。为了实现借贷收益,ibTokens 必须被提取,将原生代币 + 收到的利息赎回。您将会注意到,ibToken 的兑换价格在提款时会更高,这代表利息已经被产生。因此,您将在提款时收到额外的原生代币。

从存款池中赎回原生代币时,需要提取的 ibTokens 数量必须要提供给 Vault 合约的 withdraw 方法。

// Solidity
address vaultContractAddress = '0xd7D069493685A581d27824Fc46EdA46B7EfC0063'; // BNB Vault
IVault(vaultContractAddress).withdraw(ibTokenAmount);

原生代币将会返回给调用者。如果返回的代币是 BNB,Vault 合约将为 WBNB 解除包装为 BNB。

计息代币的计算

计息代币(ibToken)是代表存款人在存款池份额的代币。 ibTokens 将随着时间的推移从借贷中获得利息。任何希望使用 Alpaca Finance 存款池的 DeFi 协议都应该正确理解和实施 ibTokens 的计算,以反映用户在存款池的实际份额。

因此,当存款池首次上线时,ibToken 与实际原生代币之间的比率才会为 1:1,之后 ibToken 的价值将随着借贷利息的累计而继续增加(ibToken 相对于原生代币的价值将仅单向提升。价值不会减少)。例如,让我们假设价格: 1 ibBNB = 1.0292 BNB ,这代表着存款 1.0292 BNB将获得 1 ibBNB 作为回报。另一方面,兑换1 ibBNB 将获得1.0292 BNB 作为回报。赎回 ibBNB 时的额外 BNB 来自累计的利息。

了解 ibTokens 价值的这一基本机制非常重要,因为与 Alpaca Finance 的借贷存款池集成的DeFi协议可能需要准确计算用户在存款池中的份额。否则将可能带来安全风险和经济损失。bEarn.fi ValueDeFi 被攻击的情况就是这样,这些协议始终将 ibTokens 和相关代币的的比率理解为 1:1 (阅读对 bEarn.fi ValueDeFi 的攻击的分析)。

此外,虽然 Alpaca 不支持闪电贷 (flash loans),但在外部非 Alpaca 存款池中,可能存在透过攻击来操纵 ibToken 价格的风险,变相的闪电贷攻击。如果没有准备,ibToken 的价格可能会在攻击者的交易范围内发生巨大变化。这就是为什么单单依靠智能合约来计算 ibToken 的价格是不足够的。我们建议项目有一个价格预言机 (Price Oracle) 来提供 ibTokens 与原生代币的当前比率,以防止上述此类攻击。

因此,我们将为您展示计算 ibTokens 价格的最安全和正确的方法。

直接从智能合约计算

由智能合约计算 ibToken 代币价格是最简单的方法,但为了稳健性,它不能是您唯一依赖的方法。

// Solidity
address vaultContractAddress = '0xd7D069493685A581d27824Fc46EdA46B7EfC0063'; // BNB Vault
IVault vault = IVault(vaultContractAddress);
uint256 ibTokenAmount = ...;
uint256 ibTokenPrice = vault.totalToken()).div(vault.totalSupply();
uint256 underlyingTokenAmount = ibTokenAmount.mul(ibTokenPrice);

上面的代码段落示范了通过在相关存款池中提取 totalTokentotalSupply 的数值来计算ibToken 的价格。

从 Alpaca API 中提取 ibToken 价格

Alpaca Finance 提供了一个 API 来提取当前的 ibToken 价格,通过向 endpoint /ibTokens 发送一个 GET 的 REST 请求就能提取任何 ibToken 价格。与 Alpaca API 的集成需要身份验证,因此如果您想使用这个服务,请联系我们。

API 的示例结果如下:

{
    "status": {
        "code": 1000,
        "messages": [
            "OK"
        ]
    },
    "data": [
        {
            "symbol": "ibALPACA",
            "baseTokenPerShare": "1.051108636596531492",
            "lendingApr": "0.0003638488758058",
            "stakingApr": "0.0"
        },
        {
            "symbol": "ibBNB",
            "baseTokenPerShare": "1.025808940627339553",
            "lendingApr": "9.7785294844178225",
            "stakingApr": "316693.265280905443641"
        },
        {
            "symbol": "ibBUSD",
            "baseTokenPerShare": "0.972337256352625836",
            "lendingApr": "0.368330890561667",
            "stakingApr": "2269.786601379154162"
        },
        {
            "symbol": "ibUSDT",
            "baseTokenPerShare": "0.913094694500682622",
            "lendingApr": "0.0012775198722327",
            "stakingApr": "0.0"
        },
        {
            "symbol": "ibBTCB",
            "baseTokenPerShare": "1.000006160536069502",
            "lendingApr": "6.5534019786376696",
            "stakingApr": "0.0"
        },
        {
            "symbol": "ibETH",
            "baseTokenPerShare": "0.999463386510494271",
            "lendingApr": "0.0000004209960912",
            "stakingApr": "0.0"
        }
    ]
}

通过 Alpaca 的 API 提取的价格会通过价格预言机提供基于智能合约。通过智能合约的调用来提供价格将容易受到来自客户端的注入攻击。我们强烈建议不要这样做。在这里可以查看一个简单价格预言机的例子。通过使用可信的价格预言机,您可以确保 ibToken 价格数据的完整性和可靠性。

我们强烈建议同时透过智能合约的计算和 Alpaca API 来提取 ibToken 价格。这两个来源的价格数据应该被比较以防止任何可能的数据异常。如果两个来源之间的比较差异很大,则价格数据应该被拒绝。

总之,计算 ibTokens 最安全的流程应当如下:

  1. 在您自己的合约中计算 ibTokens 的价格

  2. 利用链下预言机获取 ibTokens 的价格,或通过 Alpaca API 拉取价格

  3. 比较 1 和 2 的结果,如果相差超过 n% 则拒绝 (revert) 交易

Alpaca 的 API 目前只提供给白名单的协议和机构,想要进入白名单,请电邮联系我们 requests@alpacafinance.org

质押

根据 AIP-15,ALPACA 奖励会在 2 月份暂停。因此,以下提供的质押相关信息仅供参考,可能因 ALPACA 质押奖励暂停而不再适用。

Alpaca Finance 为用户提供质押机会。计息代币和特定的 LP 代币在我们的质押池中质押以赚取ALPACA 代币形式的额外收益。如果您已存入资金并收到 ibToken,我们建议您将这些代币放入这些池中以获得最大的奖励。

要将代币质押到 Alpaca Finance 的质押池中,首先需要获取质押池及其对应质押代币列表。请参阅 这里 有关所有质押池的合约地址。在我们的代码库中,质押池的合约通常被称为 Fairlaunch

执行质押可通过调用 deposit 方法。参数如下:

  • _for 为存款人地址。

  • _pid 为质押池的 ID。

  • _amount 是要存入的代币数量,以 uint256 表示(例如 ibBNB 使用18个位小数位,1 ibBNB = 100000000000000000001*1e18

// Solidity
address fairlaunchContractAddress = '0xA625AB01B08ce023B2a342Dbb12a16f2C8489A8F'; // ibBNB pool
IFairLaunch fairlaunch = IFairLaunch(fairlaunchContractAddress);
fairlaunch.deposit(msg.sender, poolId, amount);

质押份额的信息将被存储在智能合约状态中。质押不会发行代币。通过调用 Fairlaunch 合约中 userInfo 方法可提取用户的份额。

来自质押的奖励不会自动被计算,而必须手动收取。但是,在已质押的池上进行存款或取款将自动收割待提取奖励到调用者。要从池中收割奖励,调用者必须调用 Fairlaunch 合约中的 harvest 方法并提供质押池 ID。

// Solidity
address fairlaunchContractAddress = '0xA625AB01B08ce023B2a342Dbb12a16f2C8489A8F'; // ibBNB pool
IFairLaunch fairlaunch = IFairLaunch(fairlaunchContractAddress);
fairlaunch.harvest(poolId);

要从池中提取质押代币,必须在 Fairlaunch 合约中的 withdraw 方法中提供用户份额的数量。

// Solidity
address fairlaunchContractAddress = '0xA625AB01B08ce023B2a342Dbb12a16f2C8489A8F'; // ibBNB pool
IFairLaunch fairlaunch = IFairLaunch(fairlaunchContractAddress);
fairlaunch.withdraw(msg.sender, poolId, amount);

质押代币将返还给调用者,任何待提取的奖励也将自动被收取。

与 Alpaca Finance 测试

目前,我们不提供对测试网环境的支持。我们建议通过从主网分叉进行测试,这可以通过 Tenderly‘s Fork 轻松完成。所有相关信息和地址请参考 https://github.com/alpaca-finance/bsc-alpaca-contract/blob/main/.mainnet.json

Last updated