Skip to content

Composable flash loan implementation #70

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
352 changes: 265 additions & 87 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions token-lending/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ version = "0.1.0"

[dependencies]
clap = "=2.34.0"
solana-clap-utils = "=1.7.12"
solana-cli-config = "=1.7.12"
solana-client = "=1.7.12"
solana-logger = "=1.7.12"
solana-sdk = "=1.7.12"
solana-program = "=1.7.12"
solana-clap-utils = "=1.8.14"
solana-cli-config = "=1.8.14"
solana-client = "=1.8.14"
solana-logger = "=1.8.14"
solana-sdk = "=1.8.14"
solana-program = "=1.8.14"
spl-token-lending = { path="../program", features = [ "no-entrypoint" ] }
spl-token = { version = "3.2.0", features=["no-entrypoint"] }

Expand Down
6 changes: 3 additions & 3 deletions token-lending/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ arrayref = "0.3.6"
bytemuck = "1.5.1"
num-derive = "0.3"
num-traits = "0.2"
solana-program = "=1.7.12"
solana-program = "=1.8.14"
spl-token = { version = "3.2.0", features=["no-entrypoint"] }
switchboard-program = "0.2.0"
thiserror = "1.0"
Expand All @@ -27,8 +27,8 @@ assert_matches = "1.5.0"
base64 = "0.13"
log = "0.4.14"
proptest = "1.0"
solana-program-test = "=1.7.12"
solana-sdk = "=1.7.12"
solana-program-test = "=1.8.14"
solana-sdk = "=1.8.14"
serde = "1.0"
serde_yaml = "0.8"

Expand Down
21 changes: 21 additions & 0 deletions token-lending/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,31 @@ pub enum LendingError {
/// Not enough liquidity after flash loan
#[error("Not enough liquidity after flash loan")]
NotEnoughLiquidityAfterFlashLoan,

// 45
/// Null oracle config
#[error("Null oracle config")]
NullOracleConfig,
/// No cpi flash borrows allowed
#[error("No cpi flash borrows allowed")]
FlashBorrowCpi,
/// No corresponding repay found for flash borrow
#[error("No corresponding repay found for flash borrow")]
NoFlashRepayFound,
/// Invalid flash repay found for borrow
#[error("Invalid repay found for flash borrow")]
InvalidFlashRepay,
/// No cpi flash repays allowed
#[error("No cpi flash repays allowed")]
FlashRepayCpi,

// 50
/// Multiple flash borrows not allowed in the same transaction
#[error("Multiple flash borrows not allowed in the same transaction")]
MultipleFlashBorrows,
/// Flash loans are disabled for this reserve
#[error("Flash loans are disabled for this reserve")]
FlashLoansDisabled,
}

impl From<LendingError> for ProgramError {
Expand Down
116 changes: 116 additions & 0 deletions token-lending/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,45 @@ pub enum LendingInstruction {
/// Reserve config to update to
config: ReserveConfig,
},

// 17
/// Flash borrow reserve liquidity
//
/// Accounts expected by this instruction:
///
/// 0. `[writable]` Source liquidity token account.
/// 1. `[writable]` Destination liquidity token account.
/// 2. `[writable]` Reserve account.
/// 3. `[]` Lending market account.
/// 4. `[]` Derived lending market authority.
/// 5. `[]` Instructions sysvar.
/// 6. `[]` Token program id.
/// 7. `[]` Clock sysvar.
FlashBorrowReserveLiquidity {
/// Amount of liquidity to flash borrow
liquidity_amount: u64,
},

// 18
/// Flash repay reserve liquidity
//
/// Accounts expected by this instruction:
///
/// 0. `[writable]` Source liquidity token account.
/// $authority can transfer $liquidity_amount.
/// 1. `[writable]` Destination liquidity token account.
/// 2. `[writable]` Flash loan fee receiver account.
/// Must match the reserve liquidity fee receiver.
/// 3. `[writable]` Host fee receiver.
/// 4. `[writable]` Reserve account.
/// 5. `[]` Lending market account.
/// 6. `[signer]` User transfer authority ($authority).
/// 7. `[]` Instructions sysvar.
/// 8. `[]` Token program id.
FlashRepayReserveLiquidity {
/// Amount of liquidity to flash repay
liquidity_amount: u64,
},
}

impl LendingInstruction {
Expand Down Expand Up @@ -514,6 +553,14 @@ impl LendingInstruction {
},
}
}
17 => {
let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
Self::FlashBorrowReserveLiquidity { liquidity_amount }
}
18 => {
let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
Self::FlashRepayReserveLiquidity { liquidity_amount }
}
_ => {
msg!("Instruction cannot be unpacked");
return Err(LendingError::InstructionUnpackError.into());
Expand Down Expand Up @@ -692,6 +739,14 @@ impl LendingInstruction {
buf.extend_from_slice(&config.borrow_limit.to_le_bytes());
buf.extend_from_slice(&config.fee_receiver.to_bytes());
}
Self::FlashBorrowReserveLiquidity { liquidity_amount } => {
buf.push(17);
buf.extend_from_slice(&liquidity_amount.to_le_bytes());
}
Self::FlashRepayReserveLiquidity { liquidity_amount } => {
buf.push(18);
buf.extend_from_slice(&liquidity_amount.to_le_bytes());
}
}
buf
}
Expand Down Expand Up @@ -1216,3 +1271,64 @@ pub fn update_reserve_config(
data: LendingInstruction::UpdateReserveConfig { config }.pack(),
}
}

/// Creates a 'FlashBorrowReserveLiquidity' instruction.
#[allow(clippy::too_many_arguments)]
pub fn flash_borrow_reserve_liquidity(
program_id: Pubkey,
liquidity_amount: u64,
source_liquidity_pubkey: Pubkey,
destination_liquidity_pubkey: Pubkey,
reserve_pubkey: Pubkey,
lending_market_pubkey: Pubkey,
) -> Instruction {
let (lending_market_authority_pubkey, _bump_seed) = Pubkey::find_program_address(
&[&lending_market_pubkey.to_bytes()[..PUBKEY_BYTES]],
&program_id,
);

Instruction {
program_id,
accounts: vec![
AccountMeta::new(source_liquidity_pubkey, false),
AccountMeta::new(destination_liquidity_pubkey, false),
AccountMeta::new(reserve_pubkey, false),
AccountMeta::new_readonly(lending_market_pubkey, false),
AccountMeta::new_readonly(lending_market_authority_pubkey, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
],
data: LendingInstruction::FlashBorrowReserveLiquidity { liquidity_amount }.pack(),
}
}

/// Creates a 'FlashRepayReserveLiquidity' instruction.
#[allow(clippy::too_many_arguments)]
pub fn flash_repay_reserve_liquidity(
program_id: Pubkey,
liquidity_amount: u64,
source_liquidity_pubkey: Pubkey,
destination_liquidity_pubkey: Pubkey,
reserve_liquidity_fee_receiver_pubkey: Pubkey,
host_fee_receiver_pubkey: Pubkey,
reserve_pubkey: Pubkey,
lending_market_pubkey: Pubkey,
user_transfer_authority_pubkey: Pubkey,
) -> Instruction {
Instruction {
program_id,
accounts: vec![
AccountMeta::new(source_liquidity_pubkey, false),
AccountMeta::new(destination_liquidity_pubkey, false),
AccountMeta::new(reserve_liquidity_fee_receiver_pubkey, false),
AccountMeta::new(host_fee_receiver_pubkey, false),
AccountMeta::new(reserve_pubkey, false),
AccountMeta::new_readonly(lending_market_pubkey, false),
AccountMeta::new_readonly(user_transfer_authority_pubkey, true),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: LendingInstruction::FlashRepayReserveLiquidity { liquidity_amount }.pack(),
}
}
Loading