Skip to content
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

0xripleys manual liquidate #112

Merged
merged 7 commits into from
Nov 17, 2022
Merged
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
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