{"content":{"title":"sui move中闪贷的实现（flash lender）","body":"## 什么是闪贷？\r\n  \r\n闪贷是用户通过智能合约，无需任何许可立刻贷出其中的货币进行使用，通过套利赚取收益，然后将本金与相应的闪贷费用偿还给合约。要求这些操作必须在一个交易中完成，如果在交易的最后，闪贷者为偿还足够的本金与费用，交易将会回滚，闪贷者还需支付额外的gas费用。  \r\n  \r\n## sui move中闪贷的实现\r\n\r\n让我们阅读move官方代码库中的案例，了解sui move中如何实现闪贷。  \r\n有三个重要结构：  \r\n* FlashLender\r\n\r\n```solidity\r\n    struct FlashLender<phantom T> has key {\r\n        id: UID,\r\n        /// Coins available to be lent to prospective borrowers\r\n        to_lend: Balance<T>,\r\n        /// Number of `Coin<T>`'s that will be charged for the loan.\r\n        /// In practice, this would probably be a percentage, but\r\n        /// we use a flat fee here for simplicity.\r\n        fee: u64,\r\n    }\r\n````\r\n拥有key属性，这个obj可以理解为储存闪贷金额的金库  \r\n\r\n* Receipt\r\n```solidity\r\n    struct Receipt<phantom T> {\r\n        /// ID of the flash lender object the debt holder borrowed from\r\n        flash_lender_id: ID,\r\n        /// Total amount of funds the borrower must repay: amount borrowed + the fee\r\n        repay_amount: u64\r\n    }\r\n```\r\n\"烫手山芋\"模式的应用，当FlashLender中的代币被借出，应该创建相应的Receipt，由于它不能被储存，所以在有Receipt创建的交易中必须最后被解构，由于Receipt在贷款函数中被创建，在还款函数中被解构，这正好对应了贷款还款在一个交易中实现，交易才能顺利进行。  \r\n  \r\n\r\n* AdminCap\r\n```solidity\r\n    struct AdminCap has key, store {\r\n        id: UID,\r\n        flash_lender_id: ID,\r\n    }\r\n```\r\n标识了对应flash_lender_id的所有权，只有AdminCap的所有者才能提取对应id的flash_lender中的本金和收取的交易费。  \r\n\r\n再来看函数实现  \r\n* 创建闪贷池\r\n```solidity\r\n    public fun new<T>(to_lend: Balance<T>, fee: u64, ctx: &mut TxContext): AdminCap {\r\n        let id = object::new(ctx);\r\n        let flash_lender_id = object::uid_to_inner(&id);\r\n        let flash_lender = FlashLender { id, to_lend, fee };\r\n        // make the `FlashLender` a shared object so anyone can request loans\r\n        transfer::share_object(flash_lender);\r\n\r\n        // give the creator admin permissions\r\n        AdminCap { id: object::new(ctx), flash_lender_id }\r\n    }\r\n\r\n    /// Same as `new`, but transfer `AdminCap` to the transaction sender\r\n    public entry fun create<T>(to_lend: Coin<T>, fee: u64, ctx: &mut TxContext) {\r\n        let balance = coin::into_balance(to_lend);\r\n        let admin_cap = new(balance, fee, ctx);\r\n\r\n        transfer::public_transfer(admin_cap, tx_context::sender(ctx))\r\n    }\r\n```\r\n调用create函数，会创建一个flash_lender然后使它成为share_obj,lender中的代币数量和闪贷费可以自己决定。然后创建Cap转给交易的发起者。  \r\nflash_lender的创建逻辑在new函数中，他不能被交易直接调用，所以如果我们想对Cap实现更复杂的所有权转移，可以选择合约来调用它来创建flash_lender和adminCap。  \r\n\r\n* 变更闪贷池信息\r\n\r\n```solidity\r\n    public fun withdraw<T>(\r\n        self: &mut FlashLender<T>,\r\n        admin_cap: &AdminCap,\r\n        amount: u64,\r\n        ctx: &mut TxContext\r\n    ): Coin<T> {\r\n        // only the holder of the `AdminCap` for `self` can withdraw funds\r\n        check_admin(self, admin_cap);\r\n\r\n        let to_lend = &mut self.to_lend;\r\n        assert!(balance::value(to_lend) >= amount, EWithdrawTooLarge);\r\n        coin::take(to_lend, amount, ctx)\r\n    }\r\n\r\n    /// Allow admin to add more funds to `self`\r\n    public entry fun deposit<T>(\r\n        self: &mut FlashLender<T>, admin_cap: &AdminCap, coin: Coin<T>\r\n    ) {\r\n        // only the holder of the `AdminCap` for `self` can deposit funds\r\n        check_admin(self, admin_cap);\r\n        coin::put(&mut self.to_lend, coin);\r\n    }\r\n\r\n    /// Allow admin to update the fee for `self`\r\n    public entry fun update_fee<T>(\r\n        self: &mut FlashLender<T>, admin_cap: &AdminCap, new_fee: u64\r\n    ) {\r\n        // only the holder of the `AdminCap` for `self` can update the fee\r\n        check_admin(self, admin_cap);\r\n\r\n        self.fee = new_fee\r\n    }\r\n\r\n    fun check_admin<T>(self: &FlashLender<T>, admin_cap: &AdminCap) {\r\n        assert!(object::borrow_id(self) == &admin_cap.flash_lender_id, EAdminOnly);\r\n    }\r\n```\r\n仅有闪贷池流动性的提供者，也就是持有AdminCap的人，才能管理相应id的闪贷池信息，对资金进行操作。可以调用deposit追加闪贷池流动性，调用withdraw撤回部分或全部流动性，还可以更新闪贷费。  \r\n在这些操作之前，都会检查AdminCap对应的闪贷池id是否正确，以免影响不属于自己的闪贷池。  \r\n\r\n* 闪贷过程\r\n\r\n```solidity\r\n    public fun loan<T>(\r\n        self: &mut FlashLender<T>, amount: u64, ctx: &mut TxContext\r\n    ): (Coin<T>, Receipt<T>) {\r\n        let to_lend = &mut self.to_lend;\r\n        assert!(balance::value(to_lend) >= amount, ELoanTooLarge);\r\n        let loan = coin::take(to_lend, amount, ctx);\r\n        let repay_amount = amount + self.fee;\r\n        let receipt = Receipt { flash_lender_id: object::id(self), repay_amount };\r\n\r\n        (loan, receipt)\r\n    }\r\n\r\n    /// Repay the loan recorded by `receipt` to `lender` with `payment`.\r\n    /// Aborts if the repayment amount is incorrect or `lender` is not the `FlashLender`\r\n    /// that issued the original loan.\r\n    public fun repay<T>(self: &mut FlashLender<T>, payment: Coin<T>, receipt: Receipt<T>) {\r\n        let Receipt { flash_lender_id, repay_amount } = receipt;\r\n        assert!(object::id(self) == flash_lender_id, ERepayToWrongLender);\r\n        assert!(coin::value(&payment) == repay_amount, EInvalidRepaymentAmount);\r\n\r\n        coin::put(&mut self.to_lend, payment)\r\n    }\r\n```\r\n与闪贷过程相关的只有这两个函数，要解释的是，由于闪贷需要包含借贷，套利，还款等步骤，并且要求在一个交易中实现，所以我们需要通过合约来进行交互。以下是一个简略的交互例子：  \r\n```solidity\r\n    public entry fun do_flash_loan(self: &mut FlashLender<T>, amount: u64,ctx: &mut TxContext) {\r\n        //闪贷\r\n        let (loan,receipt) = flash_lender::loan(self,amount,ctx);\r\n\r\n        //套利\r\n        //1.使用借来的lone进行套利，获得更多的coin\r\n        //2.从获得的coin中分出balance与repay_amount相等的payment_coin\r\n\r\n        //还款\r\n        flash_lender::repay(self,payment_coin,receipt);\r\n        //对套利所得的coin进行所有权处理\r\n    }\r\n```\r\n这就是官方代码库中闪贷在sui move的基本实现，希望有所帮助。\r\n\r\n<!--StartFragment-->\r\n\r\nMove语言学习交流QQ群: 79489587\\\r\nSui官方中文开发者电报群: <https://t.me/sui_dev_cn>\r\n\r\n<!--EndFragment-->"},"author":{"user":"https://learnblockchain.cn/people/18488","address":null},"history":"bafkreiebclnla5h6wmarkpmk5i4orwmbmahygczfnoqq34cxdycscg4h6q","timestamp":1709394051,"version":1}