Flashloans

What is a Flash Loan?

Flash Loans allow you to borrow any available amount of assets without putting up any collateral, as long as the liquidity is returned to the protocol within the same transaction. To do a Flash Loan, you must build a contract requesting a Flash Loan. The contract will then need to execute the instructed steps and pay back the loan + fees all within the same transaction.

How do we support Flash Loans?

With Move, we implement flash loans using the hot-potato pattern. Let's go through the code to better understand it.

First, we define a Flashloan struct without any abilities.

struct Flashloan<phantom Asset0, phantom Asset1, phantom Asset2, phantom Asset3> {
  amount_0: u64,
  amount_1: u64,
  amount_2: u64,
  amount_3: u64,
}

The struct records the number of Coin<T>'s that were borrowed. Because this struct does not have the key or store ability, it cannot be transferred or otherwise placed in persistent storage. Because it does not have the drop ability, it cannot be discarded. Thus, the only way to get rid of this struct is to call repay sometime during the transaction that created it, which is exactly what we want from a flash loan. Otherwise, your code won't even compile.

Flash Loan fee

Meridian charges a fixed 1bps flash loan fee.

Utilizing Flash Loans

Call flashloan and pay_flashloan in the same transaction. Here's a complete example:

script {
    use bridge::asset::USDC;
    use bridge::asset::USDT;

    use aptos_std::math64;
    use aptos_framework::coin;

    use thalaswap::base_pool::Null;
    use thalaswap::stable_pool;
    use thalaswap::weighted_pool::{Self, Weight_50};

    const FLASHLOAN_AMOUNT: u64 = 10000;
    const FLASHLOAN_FEE_BPS: u64 = 1;
    const BPS_BASE: u64 = 10000;

    fun main(account: &signer) {
        // stable pool example
        // 1. acquire flashloan
        let (coin_0, coin_1, coin_2, coin_3, flashloan) = stable_pool::flashloan<USDC, USDT, Null, Null>(FLASHLOAN_AMOUNT, 0, 0, 0);

        // 2. do something with the coins

        // 3. calculate fee amount
        let fee_amount = math64::mul_div(FLASHLOAN_AMOUNT, FLASHLOAN_FEE_BPS, BPS_BASE);
        // alternatively, you can use `flashloan_fee_bps` view function to get the fee bps
        // let fee_amount = math64::mul_div(FLASHLOAN_AMOUNT, stable_pool::flashloan_fee_bps<USDC, USDT, Null, Null>(), BPS_BASE);

        // 4. repay flashloan
        let fee = coin::withdraw<USDC>(account, fee_amount);
        coin::merge(&mut coin_0, fee);
        stable_pool::pay_flashloan<USDC, USDT, Null, Null>(coin_0, coin_1, coin_2, coin_3, flashloan);
        
        // weighted pool example
        // 1. acquire flashloan
        let (coin_0, coin_1, coin_2, coin_3, flashloan) = weighted_pool::flashloan<USDC, USDT, Null, Null, Weight_50, Weight_50, Null, Null>(FLASHLOAN_AMOUNT, 0, 0, 0);

        // 2. do something with the coins

        // 3. calculate fee amount
        let fee_amount = math64::mul_div(FLASHLOAN_AMOUNT, FLASHLOAN_FEE_BPS, BPS_BASE);
        // alternatively, you can use `flashloan_fee_bps` view function to get the fee bps
        // let fee_amount = math64::mul_div(FLASHLOAN_AMOUNT, weighted_pool::flashloan_fee_bps<USDC, USDT, Null, Null, Weight_50, Weight_50, Null, Null>(), BPS_BASE);

        // 4. repay flashloan
        let fee = coin::withdraw<USDC>(account, fee_amount);
        coin::merge(&mut coin_0, fee);
        weighted_pool::pay_flashloan<USDC, USDT, Null, Null, Weight_50, Weight_50, Null, Null>(coin_0, coin_1, coin_2, coin_3, flashloan);
    }
}

Last updated