# 用 Python 开发 DeFi 去中心化借贷应用（中）

By [quantbang](https://paragraph.com/@quantbang) · 2022-03-24

---

![](https://storage.googleapis.com/papyrus_images/6878520b4315482ce3c120e90d4d0d5121242c74a1e959e8196ab638e9294d94.jpg)

传统的金融科技世界充满了使用户能够制作复杂算法交易模型和系统的工具。而去中心化金融 (DeFi) 也同样为用户和开发人员提供了相同的工具，围绕底层金融协议和工具具有更高的透明度和灵活性，从而催生了 DeFi 量化交易和应用开发。DeFi 开发人员和 DeFi quants 甚至可以利用这些工具的衍生品，并将它们组合成新的服务，以建立在传统金融科技世界中没有的创新金融头寸。DeFi 开发人员的基本工具之一是能够以非托管方式借出和借入加密货币资产。阅读本文前请先查看：

[《用 Python 开发 DeFi 去中心化借贷应用（上）》](http://mp.weixin.qq.com/s?__biz=MzAxMjUyNDQ5OA%3D%3D&chksm=806e7382b719fa94b728a56f3034317036624771ca2ce60db4715b808cfcf3c1af5b21e7aa35&idx=1&mid=2653576511&scene=21&sn=f801e6fd57da1609a8a4f361d8fbc6cb#wechat_redirect)

**存入抵押品**

接下来，我们运行 `aave_borrow.py` 脚本，它做了 4 件事：

•将抵押品存入 Aave 贷款池•获得了我们的抵押品和另一种资产之间的对话率•使用该抵押品借入不同的资产（贷款）•还清了贷款

我们看一下`main`函数的开头：

    def main():
       account = get_account()
       erc20_address = config["networks"][network.show_active()]["weth_token"]
       if network.show_active() in ["mainnet-fork"]:
           get_weth(account=account)
       lending_pool = get_lending_pool()
    

我们首先从配置文件中获取我们的地址（如果我们在测试网上，它会再次从我们的配置中提取）。如果您在本地链上进行测试，我们只使用它生成的第一个帐户。这在 `get_account` 函数中定义。然后，我们也从配置文件中获取 WETH 令牌的地址，如果我们在本地链上，我们调用之前调用的相同 `get_weth` 函数。这是为了确保如果我们使用本地链，我们的钱包中有 WETH。

最后，我们得到了`lending_pool`的地址。 `lending_pool`是管理借贷功能的链上智能合约，因此得名。您可以在 Aave 文档中查看功能，或者您可以直接在链上查看合约。

我们在这个合约中看到了许多有用的功能：

•借贷•抵押•获取用户帐户数据•偿还

这些是我们将要使用的函数。请记住，链上智能合约与传统软件工程中的对象或类相同，每个都有与之相关的功能。如果您要`print(type(lending_pool))`，我们会得到这种类型：

    <class 'brownie.network.contract.Contract'>
    

这意味着借贷池是一个合约，就像 WETH 一样！我们的 `get_lending_pool()` 函数实际上做了一些额外的步骤来获取贷款池合约。让我们看看这个函数：

    def get_lending_pool():
       lending_pool_addresses_provider = interface.ILendingPoolAddressesProvider(
           config["networks"][network.show_active(
           )]["lending_poll_addresses_provider"]
       )
       lending_pool_address = lending_pool_addresses_provider.getLendingPool()
       lending_pool = interface.ILendingPool(lending_pool_address)
       return lending_pool
    

我们需要两个东西才能与智能合约进行交互：

•ABI/接口•地址

我们有位于`interfaces`文件夹中的借贷池的接口。你会注意到一个流行的接口约定是让它们以字母“I”开头。我们在这个例子中有一个mix，表示它不必以“I”开头。

为了让我们获得借贷池的地址，我们实际上必须通过 `LendingPoolAddressesProvider` 的地址。这是一个链上合约，其中包含 Aave 借贷池的所有地址。有时，出借池地址发生变化，但出借池提供者地址永远不会改变，因此我们始终可以使用该合约的地址来获取出借池地址。你会看到我们在`lenting_pool_addresses_provider` 变量上调用了 `getLendingPool` 函数。

让我们回到主函数。在我们得到了贷款池合约的地址后，我们要存入抵押品。这是我们需要做的第一步，因为只有放置了抵押品，我们才能获得贷款。我们也可以假设只存入抵押品！存入抵押品将使我们获得：

1、收益

该协议向将抵押品存入平台的用户支付报酬。

2、取出贷款

我们只有在有足够的抵押品的情况下才能贷款/借入。Aave 有一个名为 `Liquidationcall` 的函数，如果您没有足够的抵押品，任何人都可以调用该函数并获得奖励。抵押品的价值必须保持大于贷款的价值，这就是为什么这被称为超额抵押贷款。

3、流动性挖矿

也称为“收益耕作”，流动性挖矿是一种 DeFi 机制，它使用户能够以治理或其他类型代币的形式获得奖励，以向协议提供流动性。但是，为了让我们存入一些代币，我们必须批准智能合约才能将代币从我们的钱包中取出。我们将把我们的 WETH 作为抵押品。

    approve_erc20(amount, lending_pool.address, erc20_address, account)
    

让我们看看我们的`approve_erc20`函数。

    def approve_erc20(amount, lending_pool_address, erc20_address, account):
       print("Approving ERC20...")
       erc20 = interface.IERC20(erc20_address)
       tx_hash = erc20.approve(lending_pool_address, amount, {"from": account})
       tx_hash.wait(1)
       print("Approved!")
       return True
    

•`amount`：允许协议使用多少 ERC20，以 wei 为单位。 `wei` 是区块链世界中最小的计量单位。1 ETH == 1000000000000000000wei•`lending_pool_address`：借贷池地址。•`erc20_address`：ERC20 代币的地址。•`account`：我们想要交易的账户。

在使用 ERC20 代币时，如果我们希望从我们的钱包中“调用”另一个合约，我们必须首先批准该合约。我们调用 ERC20 合约（我们的 WETH）上的批准函数，然后我们“等待”该交易继续进行。

    tx_hash.wait(1)
    

现在我们已经批准了合同，我们可以将我们的抵押品存入我们的主函数中的 `Lotion_pool` 合同中。

    print("Depositing...")
       lending_pool.deposit(erc20_address, amount, account.address, 0, {"from": account})
       print("Deposited!")
    

我们可以在这里看到，我们在合约上调用了`deposit`函数，我们给了它：

•`erc20_address`：要存入的代币（在我们的例子中是 WETH）•`amount`：要存入的 wei 金额。•`account.address`：我们想为谁贷款，在这种情况下是我们自己。•`0`：这是推荐代码（已折旧，始终使用 0）

我们可以从 Aave 文档中了解更多关于智能合约中的存款功能。

如果你正确地完成了这部分，你会得到一个交易哈希，它会告诉你发生了什么。让我们看一个关于 Etherscan 上 Kovan 测试网的例子。

![](https://storage.googleapis.com/papyrus_images/ceb15e77f958d4a14ec0efa680929a8c2815de89ec1b35c247de39e7251a354e.png)

让我们深入了解发生了什么。如果我们查看 `Tokens Transferred` 部分，我们可以看到 1 笔存款交易发生了 3 笔交易。按顺序，这些是：

•一些 `aToken` 被铸造出来供我们存入 (aWETH)•我们发送了 1 个 WETH 到 lending\_pool 合约（这是我们的存款）•我们收到了一些 `aToken`

所以中间代币转移是有道理的——我们正在向借贷池发送一些 WETH。但是其他`token`是什么？

`aToken` 是一种计息代币，在存款时铸造并在赎回时销毁。所以当你取回你的存款时，你烧掉你的 `aToken` 以获得等量的基础资产。 `aTokens` 最酷的部分是它们的价值每时每刻都在上涨！余额增加是因为人们使用 Aave 协议并借用您的资产，因此您因此获得报酬！

您可以查看某人的余额并每隔几秒钟刷新一次，以实时查看余额的增加情况。

每当我们想要赎回或提取我们的 WETH 时，无论我们拥有多少 `aWETH`，我们都会提取，而这些 `aWETH` 将被烧毁。

与 WETH token相同，您现在可以在您的 MetaMask 钱包中看到 aWETH。只需进入您的 MetaMask，并执行我们上面所做的添加的token部分，Kovan WETH 地址为 `0x87b1f4cf9bd63f7bbd3ee1ad04e8f52540349347`。

**抵押/贷款**

现在我们已经存入了一些抵押品，我们可以贷款了。现在我们为什么要贷款？

•无摩擦卖空•无需平仓即可获得流动性

您可以借入资产以保持对标的抵押品的敞口，同时还可以用另一种资产进行交易或支付。只要确保将您的健康系数保持在 1 以上。

![](https://storage.googleapis.com/papyrus_images/1cdab3e45dd76c6e2103c6f6c96f2541b10044b363615945a2b153faba7c9b38.png)

![](https://storage.googleapis.com/papyrus_images/265cc75d3207e0159737f8ed24f27e58567a63130ffaba33b8b67a697fdec53e.png)

您的健康因素是您贷款的抵押品数量。假设我存入了 1 ETH，借了 0.5 ETH。如果清算阈值为 1，那么我的健康系数将为 2。

清算门槛是贷款被定义为抵押不足的百分比。例如，清算门槛为 80% 意味着如果价值超过抵押品的 80%，则贷款抵押不足，可以清算。

但是假设我有 2 ETH 借入和 1 ETH 作为抵押品，1 作为清算门槛。我的健康系数是 0.5，我会被要求清算。这是清算人代表借款人偿还部分或全部未偿还借入金额的情况，同时获得折扣金额的抵押品作为回报（也称为清算“奖金”）。清算人可以决定他们是否想要获得等值的金额 抵押aToken，或者直接标的资产，当清算成功完成后，增加仓位的健康系数，使健康系数在1以上。通过这种方式，来保持贷款健康！

以我们的抵押品借款，我们首先要衡量我们作为抵押品的抵押品数据。我们可以调用贷款池上的 `getUserAccountData` 函数来找出这一点。

我们将它封装到一个名为 `get_borrowable_data` 的函数中

    borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account)
    

    def get_borrowable_data(lending_pool, account):
       (
           total_collateral_eth,
           total_debt_eth,
           available_borrow_eth,
           current_liquidation_threshold,
           tlv,
           health_factor,
       ) = lending_pool.getUserAccountData(account.address)
       available_borrow_eth = Web3.fromWei(available_borrow_eth, "ether")
       total_collateral_eth = Web3.fromWei(total_collateral_eth, "ether")
       total_debt_eth = Web3.fromWei(total_debt_eth, "ether")
       print(f"You have {total_collateral_eth} worth of ETH deposited.")
       print(f"You have {total_debt_eth} worth of ETH borrowed.")
       print(f"You can borrow {available_borrow_eth} worth of ETH.")
       return (float(available_borrow_eth), float(total_debt_eth))
    

`getUserAccountData` 返回：

•`totalCollateralETH`•`totalDebtETH`•`availableBorrowsETH`•`currentLiquidationThreshold`•`Ltv`•`healthFactor`

您可以在 Aave 文档中阅读这些函数。我们只关注 `available_borrow_eth` 和 `total_debt_eth`，这样我们就会知道：

•我们可以借多少钱•我们目前有多少债务

**为什么我还没有进行交易？**

您可能已经注意到，这是对智能合约进行函数调用，而我们上面刚刚说过，为了调用函数，我们必须进行交易，那么我们给出了什么呢？其实有两种类型的智能合约功能，您无需进行交易即可调用。

•视图函数•纯函数

这些函数不会修改区块链的状态，因此我们可以调用它们，因为我们只是在读取智能合约的状态。请记住，如果您修改区块链的状态，它只会花费 gas。

回到贷款。每项资产都有不同的贷款借入比率，它告诉您的清算门槛，以便您借入资产。

一旦我们得出了可借贷款量，我们就可以借了！

    borrowable_eth, total_debt_eth = get_borrowable_data(lending_pool, account)
    print(f"LETS BORROW IT ALL")
    erc20_eth_price = get_asset_price()
    amount_erc20_to_borrow = (1 / erc20_eth_price) * (borrowable_eth * 0.95)
    print(f"We are going to borrow {amount_erc20_to_borrow} DAI")
    borrow_erc20(lending_pool, amount_erc20_to_borrow, account)

---

*Originally published on [quantbang](https://paragraph.com/@quantbang/python-defi-3)*
