diff --git a/README.md b/README.md index 23fa566..6093d3d 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,8 @@ Introductory course to the Move language maintained by [Sui Foundation](https:// - [Marketplace Contract](./unit-four/lessons/4_marketplace_contract.md) - [Deployment and Testing](./unit-four/lessons/5_deployment_and_testing.md) - **Unit Five: Sui Kiosk** - - [Programmable Transaction Block](./unit-five/lessons/1_programmable_transaction_block.md) - - [Hot Potato Design Pattern](./unit-five/lessons/2_hot_potato_pattern.md) - - [Sui Kiosk](./unit-five/lessons/3_kiosk.md) + - [Hot Potato Design Pattern](./unit-five/lessons/1_hot_potato_pattern.md) + - [Sui Kiosk](./unit-five/lessons/2_kiosk.md) - **Advanced Topics** - [BCS Encoding](./advanced-topics/BCS_encoding/lessons/BCS_encoding.md) diff --git a/unit-five/example_projects/flashloan/sources/flashloan.move b/unit-five/example_projects/flashloan/sources/flashloan.move index 5aabb40..aa31f52 100644 --- a/unit-five/example_projects/flashloan/sources/flashloan.move +++ b/unit-five/example_projects/flashloan/sources/flashloan.move @@ -6,7 +6,7 @@ module flashloan::flashloan { use sui::sui::SUI; use sui::coin::{Self, Coin}; use sui::balance::{Self, Balance}; - use sui::object::{UID}; + use sui::object::{Self, UID}; use sui::tx_context::{TxContext}; // === Errors === @@ -27,16 +27,22 @@ module flashloan::flashloan { /// A loan position. /// This is a hot potato struct, it enforces the users - /// to repay the loan in the end of the transaction or within the same PTB. + /// to repay the loan in the end of the transaction. struct Loan { amount: u64, } + /// Example NFT for demonstration purpose + struct NFT has key, store { + id: UID, + price: Balance, + } + // === Public-Mutative Functions === /// Function allows users to borrow from the loan pool. /// It returns the borrowed [`Coin`] and the [`Loan`] position - /// enforcing users to fulfill before the PTB ends. + /// enforcing users to fulfill before the transaction ends. public fun borrow(pool: &mut LoanPool, amount: u64, ctx: &mut TxContext): (Coin, Loan) { assert!(amount <= balance::value(&pool.amount), ELoanAmountExceedPool); @@ -56,4 +62,31 @@ module flashloan::flashloan { balance::join(&mut pool.amount, coin::into_balance(payment)); } + + /// Buy a NFT + public fun buy_nft(payment: Coin, ctx: &mut TxContext): NFT { + NFT { + id: object::new(ctx), + price: coin::into_balance(payment), + } + } + + /// Sell a NFT + public fun sell_nft(nft: NFT, ctx: &mut TxContext): Coin { + let NFT {id, price} = nft; + object::delete(id); + coin::from_balance(price, ctx) + } + + /// Flashloan + public fun flashloan(pool: &mut LoanPool, amount: u64, ctx: &mut TxContext) { + let (loanCoin, loan) = borrow(pool, amount, ctx); + + /// We can call multiple functions in-between `borrow()` and `repay()` to use the loan for our own utility. + /// We demonstrate this behavior by buying a NFT and sell it instantly to repay the debt + let nft = buy_nft(loanCoin, ctx); + let repayCoin = sell_nft(nft, ctx); + + repay(pool, loan, repayCoin); + } } \ No newline at end of file diff --git a/unit-five/lessons/2_hot_potato_pattern.md b/unit-five/lessons/1_hot_potato_pattern.md similarity index 50% rename from unit-five/lessons/2_hot_potato_pattern.md rename to unit-five/lessons/1_hot_potato_pattern.md index c805604..cde15ae 100644 --- a/unit-five/lessons/2_hot_potato_pattern.md +++ b/unit-five/lessons/1_hot_potato_pattern.md @@ -1,6 +1,8 @@ # Hot Potato Pattern -A hot potato is a struct that has no capabilities, therefore you can only pack and unpack it in its module. The Hot Potato Pattern leverages the PTB mechanics and is commonly used in cases when the application wants to enforce users to fulfill determined business logic before the transaction ends. In simpler terms, if a hot potato value is returned by the transaction command A, you must consume it in any subsequent command B within the same PTB. The most popular use case of Hot Potato Pattern is flashloan. +A hot potato is a struct that has no capabilities, therefore you can only pack and unpack it in its module. The Hot Potato Pattern is commonly used in cases when the application wants to enforce users to fulfill determined business logic before the transaction ends. It is also usually used in conjunction with Programmable Transaction Block (PTB). The most popular use case of Hot Potato Pattern is flashloan. + +*💡Note: Read more details about [Programmable Transaction Block (PTB) here](./programmable_transaction_block.md)* ## Type Definitions @@ -27,7 +29,7 @@ module flashloan::flashloan { /// A loan position. /// This is a hot potato struct, it enforces the users - /// to repay the loan in the end of the transaction or within the same PTB. + /// to repay the loan in the end of the transaction. struct Loan { amount: u64, } @@ -41,7 +43,7 @@ We have a `LoanPool` shared object acting as a money vault ready for users to bo ```rust /// Function allows users to borrow from the loan pool. /// It returns the borrowed [`Coin`] and the [`Loan`] position -/// enforcing users to fulfill before the PTB ends. +/// enforcing users to fulfill before the transaction ends. public fun borrow(pool: &mut LoanPool, amount: u64, ctx: &mut TxContext): (Coin, Loan) { assert!(amount <= balance::value(&pool.amount), ELoanAmountExceedPool); @@ -69,5 +71,42 @@ public fun repay(pool: &mut LoanPool, loan: Loan, payment: Coin) { } ``` -Users at some point must `repay()` the loan before the PTB ends. We consume the `Loan` by unpacking it, otherwise, you will receive compiler error if you use its fields with direct access `loan.amount` as `Loan` is non-`drop`. After unpacking, we simply use the loan amount to perform valid payment check and update the `LoanPool` accordingly. +Users at some point must `repay()` the loan before the transaction ends. We consume the `Loan` by unpacking it, otherwise, you will receive compiler error if you use its fields with direct access `loan.amount` as `Loan` is non-`drop`. After unpacking, we simply use the loan amount to perform valid payment check and update the `LoanPool` accordingly. + +## Flashloan + +```rust +/// Example NFT for demonstration purpose +struct NFT has key, store { + id: UID, + price: Balance, +} + +/// Buy a NFT +public fun buy_nft(payment: Coin, ctx: &mut TxContext): NFT { + NFT { + id: object::new(ctx), + price: coin::into_balance(payment), + } +} + +/// Sell a NFT +public fun sell_nft(nft: NFT, ctx: &mut TxContext): Coin { + let NFT {id, price} = nft; + object::delete(id); + coin::from_balance(price, ctx) +} + +/// Flashloan +public fun flashloan(pool: &mut LoanPool, amount: u64, ctx: &mut TxContext) { + let (loanCoin, loan) = borrow(pool, amount, ctx); + + /// We can call multiple functions in-between `borrow()` and `repay()` to use the loan for our own utility. + /// We demonstrate this behavior by buying a NFT and sell it instantly to repay the debt + let nft = buy_nft(loanCoin, ctx); + let repayCoin = sell_nft(nft, ctx); + + repay(pool, loan, repayCoin); +``` +`flashloan()` demonstrates how we can borrow the coin and use it for our own utility before repaying the debt all in one single transaction. Between `borrow()` and `repay()`, we can freely execute any logic using the loan we just borrow. In the example, we simply buy a NFT and then sell it for profit, then, the profit is used to repay the loan. In the worst scenario where you incur a loss instead, and you can't payback the loan, then the transaction fails and no state changes to the blockchain are applied. This is a very powerful pattern as it requires you to satisfy some business logic atomically in one single transaction to prevent leaking invalid application states. \ No newline at end of file diff --git a/unit-five/lessons/3_kiosk.md b/unit-five/lessons/2_kiosk.md similarity index 100% rename from unit-five/lessons/3_kiosk.md rename to unit-five/lessons/2_kiosk.md diff --git a/unit-five/lessons/1_programmable_transaction_block.md b/unit-five/lessons/programmable_transaction_block.md similarity index 100% rename from unit-five/lessons/1_programmable_transaction_block.md rename to unit-five/lessons/programmable_transaction_block.md