Skip to content

Commit

Permalink
0xripleys manual liquidate (solana-labs#112)
Browse files Browse the repository at this point in the history
* kinda working cli liquidation

* dynamically create atas if they don't exist

* adding compute budget request

* fix clippy

* liquidate scripts

* withdraw ctokens command

* add redeem reserve collateral function
  • Loading branch information
0xripleys authored Nov 17, 2022
1 parent 3653b74 commit 9b60774
Show file tree
Hide file tree
Showing 6 changed files with 577 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions token-lending/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ solana-sdk = "=1.9.18"
solana-program = "=1.9.18"
solend-program = { path="../program", features = [ "no-entrypoint" ] }
spl-token = { version = "3.2.0", features=["no-entrypoint"] }
spl-associated-token-account = "1.0.3"

[[bin]]
name = "solend-program"
Expand Down
30 changes: 30 additions & 0 deletions token-lending/cli/scripts/liquidate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
set -ex

ETH_RESERVE=CPDiKagfozERtJ33p7HHhEfJERjvfk1VAjMXAFLrvrKP
SOL_RESERVE=8PbodeaosQP19SjYFx855UMqWxH2HynZLdBXmsrbac36
STSOL_RESERVE=5sjkv6HD8wycocJ4tC4U36HHbvgcXYqcyiPRUkncnwWs
MSOL_RESERVE=CCpirWrgNuBVLdkP2haxLTbD6XqEgaYuVXixbbpxUB6
USDC_RESERVE=BgxfHJDzm44T7XG68MYKx7YisTjZu73tVovyZSjJMpmw
USDT_RESERVE=8K9WC8xoh2rtQNY7iEGXtPvfbDCi563SdWhCAhuMP2xE
BTC_RESERVE=GYzjMCXTDue12eUGKKWAqtF5jcBYNmewr6Db6LaguEaX
RAY_RESERVE=9n2exoMQwMTzfw6NFoFFujxYPndWVLtKREJePssrKb36
SLND_RESERVE=CviGNzD2C9ZCMmjDt5DKCce5cLV4Emrcm3NFvwudBFKA

USDC_ATA=Bqn9qMjFEHRNS4wBRVAs3Uc52Dr3vm2AXJ5GaMkepBiQ
USDT_ATA=2bEeupwb9eC5R9LjCCrfetPm5yLwdGVLYng6XhNtue9H
BTC_ATA=A6Fu8DtnUqeYpzUMbZnnDpUFE5URnNUd8toZzcNBMkJ4

OBLIGATION_PUBKEY=HLRd6Dn4RUs4XbVzYhdp6UswQMCJTqWc9PgJ6VxvsyXu
# OBLIGATION_PUBKEY=3ErCznFWTRmhZE8C1mAQCkcneqcZQedB5ACqAwbbWUAP
REPAY_RESERVE=$USDC_RESERVE
WITHDRAW_RESERVE=$SOL_RESERVE
LIQUIDITY_AMOUNT=1000000000
SOURCE_LIQUIDITY=$USDC_ATA


cargo run liquidate-obligation \
--obligation $OBLIGATION_PUBKEY \
--repay-reserve $REPAY_RESERVE \
--withdraw-reserve $WITHDRAW_RESERVE \
--liquidity-amount $LIQUIDITY_AMOUNT \
--source-liquidity $SOURCE_LIQUIDITY
25 changes: 25 additions & 0 deletions token-lending/cli/scripts/withdraw.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
set -ex

ETH_RESERVE=CPDiKagfozERtJ33p7HHhEfJERjvfk1VAjMXAFLrvrKP
SOL_RESERVE=8PbodeaosQP19SjYFx855UMqWxH2HynZLdBXmsrbac36
STSOL_RESERVE=5sjkv6HD8wycocJ4tC4U36HHbvgcXYqcyiPRUkncnwWs
MSOL_RESERVE=CCpirWrgNuBVLdkP2haxLTbD6XqEgaYuVXixbbpxUB6
USDC_RESERVE=BgxfHJDzm44T7XG68MYKx7YisTjZu73tVovyZSjJMpmw
USDT_RESERVE=8K9WC8xoh2rtQNY7iEGXtPvfbDCi563SdWhCAhuMP2xE
BTC_RESERVE=GYzjMCXTDue12eUGKKWAqtF5jcBYNmewr6Db6LaguEaX
RAY_RESERVE=9n2exoMQwMTzfw6NFoFFujxYPndWVLtKREJePssrKb36
SLND_RESERVE=CviGNzD2C9ZCMmjDt5DKCce5cLV4Emrcm3NFvwudBFKA

USDC_ATA=Bqn9qMjFEHRNS4wBRVAs3Uc52Dr3vm2AXJ5GaMkepBiQ
USDT_ATA=2bEeupwb9eC5R9LjCCrfetPm5yLwdGVLYng6XhNtue9H
BTC_ATA=A6Fu8DtnUqeYpzUMbZnnDpUFE5URnNUd8toZzcNBMkJ4

OBLIGATION_PUBKEY=9XQ18M6VvB9X9QVXxj1bCvKifAW2RWDePEfBwi6fsLhq
WITHDRAW_RESERVE=$SOL_RESERVE
COLLATERAL_AMOUNT=1000


cargo run withdraw-collateral \
--obligation $OBLIGATION_PUBKEY \
--withdraw-reserve $WITHDRAW_RESERVE \
--withdraw-amount $COLLATERAL_AMOUNT \
135 changes: 135 additions & 0 deletions token-lending/cli/src/lending_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use solana_program::instruction::Instruction;
use solend_program::instruction::{
refresh_obligation, refresh_reserve, withdraw_obligation_collateral,
};
use solend_program::state::{Obligation, Reserve};

use solana_client::rpc_client::RpcClient;
use solana_program::program_pack::Pack;
use solana_program::pubkey::Pubkey;
use spl_associated_token_account::get_associated_token_address;
use std::collections::HashSet;

pub struct SolendState {
lending_program_id: Pubkey,
obligation_pubkey: Pubkey,
obligation: Obligation,
reserves: Vec<(Pubkey, Reserve)>,
}

impl SolendState {
pub fn new(
lending_program_id: Pubkey,
obligation_pubkey: Pubkey,
rpc_client: &RpcClient,
) -> Self {
let obligation = {
let data = rpc_client.get_account(&obligation_pubkey).unwrap();
Obligation::unpack(&data.data).unwrap()
};

// get reserve pubkeys
let reserve_pubkeys: Vec<Pubkey> = {
let mut r = HashSet::new();
r.extend(obligation.deposits.iter().map(|d| d.deposit_reserve));
r.extend(obligation.borrows.iter().map(|b| b.borrow_reserve));
r.into_iter().collect()
};

// get reserve accounts
let reserves: Vec<(Pubkey, Reserve)> = rpc_client
.get_multiple_accounts(&reserve_pubkeys)
.unwrap()
.into_iter()
.zip(reserve_pubkeys.iter())
.map(|(account, pubkey)| (*pubkey, Reserve::unpack(&account.unwrap().data).unwrap()))
.collect();

assert!(reserve_pubkeys.len() == reserves.len());

Self {
lending_program_id,
obligation_pubkey,
obligation,
reserves,
}
}

pub fn find_reserve_by_key(&self, pubkey: Pubkey) -> Option<&Reserve> {
self.reserves.iter().find_map(
|(p, reserve)| {
if pubkey == *p {
Some(reserve)
} else {
None
}
},
)
}

fn get_refresh_instructions(&self) -> Vec<Instruction> {
let mut instructions = Vec::new();
instructions.extend(self.reserves.iter().map(|(pubkey, reserve)| {
refresh_reserve(
self.lending_program_id,
*pubkey,
reserve.liquidity.pyth_oracle_pubkey,
reserve.liquidity.switchboard_oracle_pubkey,
)
}));

let reserve_pubkeys: Vec<Pubkey> = {
let mut r = Vec::new();
r.extend(self.obligation.deposits.iter().map(|d| d.deposit_reserve));
r.extend(self.obligation.borrows.iter().map(|b| b.borrow_reserve));
r
};

// refresh obligation
instructions.push(refresh_obligation(
self.lending_program_id,
self.obligation_pubkey,
reserve_pubkeys,
));

instructions
}

/// withdraw obligation ctokens to owner's ata
pub fn withdraw(
&self,
withdraw_reserve_pubkey: &Pubkey,
collateral_amount: u64,
) -> Vec<Instruction> {
let mut instructions = self.get_refresh_instructions();

// find repay, withdraw reserve states
let withdraw_reserve = self
.reserves
.iter()
.find_map(|(pubkey, reserve)| {
if withdraw_reserve_pubkey == pubkey {
Some(reserve)
} else {
None
}
})
.unwrap();

instructions.push(withdraw_obligation_collateral(
self.lending_program_id,
collateral_amount,
withdraw_reserve.collateral.supply_pubkey,
get_associated_token_address(
&self.obligation.owner,
&withdraw_reserve.collateral.mint_pubkey,
),
*withdraw_reserve_pubkey,
self.obligation_pubkey,
withdraw_reserve.lending_market,
self.obligation.owner,
));

instructions
}
}
Loading

0 comments on commit 9b60774

Please sign in to comment.