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