💻
与 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 集成。
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 = 10000000000000000000
或 1*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) 并将这些铸造的代币返还给调用 者。
来自贷款的利息将计入 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 来自累计的利息。此外,虽然 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);
上面的代码段落示范了通过在相关存款池中提取
totalToken
和 totalSupply
的数值来计算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 目前只提供给白名单的协议和机构,想要进入白名单,请电邮联系我们 [email protected]
Alpaca Finance 为用户提供质押机会。计息代币和特定的 LP 代币在我们的质押池中质押以赚取ALPACA 代币形式的额外收益。如果您已存入资金并收到 ibToken,我们建议您将这些代币放入这些池中以获得最大的奖励。
要将代币质押到 Alpaca Finance 的质押池中,首先需要获取质押池及其对应质押代币列表。请参阅 这里 有关所有质押池的合约地址。在我们的代码库中,质押池的合约通常被称为
Fairlaunch
。执行质押可通过调用
deposit
方法。参数如下:_for
为存款人地址。_pid
为质押池的 ID。_amount
是要存入的代币数量,以uint256
表示(例如 ibBNB 使用18个位小数位,1 ibBNB =10000000000000000000
或1*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);
质押代币将返还给调用者,任何待提取的奖励也将自动被收取。
目前,我们不提供对测试网环境的支持。我们建议通过从主网分叉进行测试,这可以通过 Tenderly‘s Fork 轻松完成。所有相关信息和地址请参考 https://github.com/alpaca-finance/bsc-alpaca-contract/blob/main/.mainnet.json
最近更新 1mo ago