本文阅读分析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-->
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!