sui move中闪贷的实现(flash lender)

  • shaflow01
  • 更新于 2024-03-02 23:40
  • 阅读 983

本文阅读分析sui官方代码库中对闪贷的实现,深入理解sui move独特合约的设计模式

什么是闪贷?

闪贷是用户通过智能合约,无需任何许可立刻贷出其中的货币进行使用,通过套利赚取收益,然后将本金与相应的闪贷费用偿还给合约。要求这些操作必须在一个交易中完成,如果在交易的最后,闪贷者为偿还足够的本金与费用,交易将会回滚,闪贷者还需支付额外的gas费用。

sui move中闪贷的实现

让我们阅读move官方代码库中的案例,了解sui move中如何实现闪贷。
有三个重要结构:

  • FlashLender
    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-->

  • 原创
  • 学分: 21
  • 分类: Sui
  • 标签:
点赞 3
收藏 2
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
shaflow01
shaflow01
0x4937...bA76
江湖只有他的大名,没有他的介绍。