Skip to content

Flash Loans

Soroban Flash Loans
#![no_std]
use soroban_sdk::{contract, contracterror, contractimpl, contracttype, token, Address, Env, log};
 
#[contracterror]
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u32)]
pub enum ReceiverError {
    InitFailed = 1,
    NotInitialized = 2,
}
 
pub trait FlashLoanReceiver {
    fn exec_op(env: Env) -> Result<(), ReceiverError>;
}
 
#[contracttype]
pub enum DataKey {
    Token,
    Amount,
    PoolAddress,
}
 
#[contract]
pub struct FlashLoanReceiverContract;
 
fn compute_fee(amount: &i128) -> i128 {
    amount / 1250 // 0.05%, still TBD
}
 
#[contractimpl]
impl FlashLoanReceiver for FlashLoanReceiverContract {
    fn exec_op(e: Env) -> Result<(), ReceiverError> {
        let token_client = if let Some(token) = &e
            .storage()
            .instance()
            .get::<DataKey, Address>(&DataKey::Token)
        {
            token::Client::new(&e, &token)
        } else {
            return Err(ReceiverError::NotInitialized);
        };
 
        let borrowed = e
            .storage()
            .instance()
            .get::<DataKey, i128>(&DataKey::Amount)
            .unwrap();
 
        let total_amount = borrowed + compute_fee(&borrowed);
 
        /// @dev should remove logs before deploying smart contracts
        log!(&e, "Total amount - {}", total_amount);
 
        let flash_loan = e
            .storage()
            .instance()
            .get::<DataKey, Address>(&DataKey::PoolAddress)
            .unwrap();
 
        token_client.approve(
            &e.current_contract_address(),
            &flash_loan,
            &total_amount,
            &(e.ledger().sequence() + 1),
        );
 
        Ok(())
    }
}
 
#[contractimpl]
impl FlashLoanReceiverContract {
    pub fn init(
        e: Env,
        token_id: Address,
        fl_address: Address,
        amount: i128,
    ) -> Result<(), ReceiverError> {
        e.storage().instance().set(&DataKey::Token, &token_id);
        e.storage()
            .instance()
            .set(&DataKey::PoolAddress, &fl_address);
        e.storage().instance().set(&DataKey::Amount, &amount);
 
        Ok(())
    }
}
 
#[cfg(test)]
mod test;

Explanation

#![no_std] This attribute prevents linking to the standard library, making the code lighter and more efficient for Soroban contracts. It's big so we save on size.

use soroban_sdk::{contract, contractimpl, Env, log} Imports stuffs from the Soroban SDK. Env is basic Soroban type, we need it because we can't use the Rust standard library.

ReceiverError This defines a custom error type with two variants - one indicating initialization failure, and another indicating the contract requiring initialization before use.

compute_fee This function calculates a fee based on the borrowed amount.

init This is used to initialize the contract, the environment, token address, flash pool address, and loan amount are taken as arguments.

exec_op This function implements the core functionality of the contract. It retrieves the stored token address, it returns an error if not found. It retrieves the borrowed amount. It calculates the total amount with the fee. It logs the total amount. It retrieves the flash loan pool address. It uses the token_client to approve the flash loan pool to withdraw the total amount plus fees. The approval is valid for the next transaction only (sequence number + 1).

Run in Playground

Loading playground...