Skip to content

CLI has a script that upgrades all reserves #215

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
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
101 changes: 101 additions & 0 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,104 @@ url = "https://api.mainnet-beta.solana.com"
[[test.validator.clone]]
# Solend Main Pool - (USDC) Reserve State
address = "BgxfHJDzm44T7XG68MYKx7YisTjZu73tVovyZSjJMpmw"
# What follows is a list of some more reserves to clone to test batch upgrade
[[test.validator.clone]]
address = "46t9bCbiBwiVsjQPz2CLYcMULCtsZTBbwqLdAz7s2xXy"
[[test.validator.clone]]
address = "6RLEnWjEUR8MTTn8MtFXmw1Fz1JWjUVcC1bQFPqDXbgy"
[[test.validator.clone]]
address = "4o8bqVMVrjwbUEU69axoQB7LFDHHt58d2XMtPXFJ8tK1"
[[test.validator.clone]]
address = "3xLmLkoKSKddqg7ejPNq679ApQNnm2dn3VVvtV7isSDo"
[[test.validator.clone]]
address = "44JpnzauMCjmwHBN1VJe4rFVjidozsAeGMR5srzuWp55"
[[test.validator.clone]]
address = "4YA39gkfuskkp3ir1NZ9jySkHYocSnZoPY2oT64BRmb5"
[[test.validator.clone]]
address = "3NdKfP3qLSzxqQTkJpYL2gGBza1esdyakRrAyMLt64R2"
[[test.validator.clone]]
address = "59KjpUPKXsoYZUNdc8g2Yi32uAw9Brm6g8bQCfFVomyJ"
[[test.validator.clone]]
address = "5noExw6LxoDaoAbcaFj5YZbBhpXoMqazzZuBrAUeFiUj"
[[test.validator.clone]]
address = "5eLCVf61tRmv4V6WASMfR7X4skqXpEakqyaLMFrQETSB"
[[test.validator.clone]]
address = "rKBpHeyPyn9YU4VNtxaX6Tu618Y2R4sWybxUpea5ph4"
[[test.validator.clone]]
address = "5yq5AJTJMoRQvGFbA2h6wKRQMV7Z6CjfbZCa3cAJS2r5"
[[test.validator.clone]]
address = "gY2sQUkxEQPDcn2s72KBKmw9q343QpVhxU5WLu2sxjr"
[[test.validator.clone]]
address = "6TsJpaAbwJMTFZdAEHcVb86zQaLoNmbabYX1kkW1NAj7"
[[test.validator.clone]]
address = "6JLJ3eq8sHDjUBLy4zjvLjvyMjrqnUDizQEdcZqgaYrG"
[[test.validator.clone]]
address = "4kgzahtogzibeopWQBrKcCYPiavEAaV9sJjuZy9dUuic"
[[test.validator.clone]]
address = "6uhoHHFPQbRpssDphCjxU6hMmXp3GLS2qAZmCExZkwap"
[[test.validator.clone]]
address = "13Ts1ERfwAM11MVQAU3zCz49fGkWmdZbfXuTGyKz6ENy"
[[test.validator.clone]]
address = "14aRZgQAQtGRES3mNTFvEVEfEqtnKJa73ryENDAvJFaQ"
[[test.validator.clone]]
address = "6e8zg2Y9skA5AMm7J62GJQ8Ui4reuzEytBS74zHV8S7A"
[[test.validator.clone]]
address = "6QNF4ovs4vWqjjvNUNoJLhXCW34iEm2QGipRKJdk725n"
[[test.validator.clone]]
address = "yjuWAA6XXhEwxWuzbPfDPnYiohCLFqAfgCDA4EdHHDm"
[[test.validator.clone]]
address = "XK8FEMEziMX9W46ivFmjddjvW3aY8dkVvucEGLmrt5D"
[[test.validator.clone]]
address = "2WBEmjZbUMXZbs9ucG3B7254y2C34d72uv2qHjvR1T4n"
[[test.validator.clone]]
address = "yxW7QwpJzKxfNo2QkbcmgjpgFxEL4UGbUivkFWtkmd3"
[[test.validator.clone]]
address = "66RtjhW1bMXTJ2ZL8TMowUTAtraHiksKGGQSArGRZKAZ"
[[test.validator.clone]]
address = "3Cv8evqFV1MWirL1ohv2VsdTAmpDvNWy4veGjYDFrWn2"
[[test.validator.clone]]
address = "5rDqwn1GMMrSkjvZS1G7BZ2tR5Q4JS13wnSHn11La9a9"
[[test.validator.clone]]
address = "4ERjFetPd5DQDK8N9wL4i36ozs4zeW1MeGbPbKw9QMsy"
[[test.validator.clone]]
address = "5hevPuvhqmXdQcuiB2mqekxQcqL9kVix8D5ckRGyA8yk"
[[test.validator.clone]]
address = "62M3oYeJ2agvuHsgHfzJuiWsTbEewZCFJbMHXKDkKMqL"
[[test.validator.clone]]
address = "6DF8vRKZKdAK2rdirJTwG3TWogjgByqcEi5WbnwXb3YZ"
[[test.validator.clone]]
address = "3mSMHPvNewL8RTwAcA6GTCLjS19J3NK2huJxgA553oHy"
[[test.validator.clone]]
address = "75EgKN1rrVssMQr6KjvR5w6Gnth8FkqECgZ6Q4mDDCx6"
[[test.validator.clone]]
address = "2tSNdecgEHEidEemg9vCbgFPzg6nyok97xJc1Lse3mG8"
[[test.validator.clone]]
address = "3Hr5qshXDQgbL1za6Sayug61Hwt5rjnV7dbyE9NUQDaZ"
[[test.validator.clone]]
address = "2v3Y19ahC3dtV6CnrmT5vZfpJy7HkyFoxkRgTeuk6cC4"
[[test.validator.clone]]
address = "53oro4QCCqqtDgfs1qfeH5LyvfdSexjXXaT14drTJ2Xj"
[[test.validator.clone]]
address = "7kL41rV8tgRBticXUyp3LfCV7eBGKUrAwL2e7GJB5ooP"
[[test.validator.clone]]
address = "7t3QnGAqse8zvAohJJX7robsBviyP5Bg7xNBxi7HSPNy"
[[test.validator.clone]]
address = "7vcWs9Gut1HE8o24cfuYkjuFCdBMEkBALqqvLDnQsBQT"
[[test.validator.clone]]
address = "2j8XVnUFk6Hxm75QdJn6MDyxVE9QCbCe5BauGg82ANZU"
[[test.validator.clone]]
address = "81EGVb5RD8yft1N3SGxitn2QnvJg7Pbq5k4CiXkf3f5A"
[[test.validator.clone]]
address = "7UgZy6RzhfSG66qrdD7Q5LHrVJRm7bUqhRHPh9siBqQQ"
[[test.validator.clone]]
address = "3kWqRMVepJ5HSmXF16bBWQYQ5C7YNGvJJqCPt3A7zKWH"
[[test.validator.clone]]
address = "8NVfgFqWPiy7B4o4yQQ8XSTwSihidtdftA1wzeePnCeJ"
[[test.validator.clone]]
address = "3738W1f4ygKayow8TrFGuDbhFovQ3QhM2MPY8AF375ki"
[[test.validator.clone]]
address = "7ssc2gVnucKKwsh6DS4HYHUWAigJomW6v37T65SXJVEr"
[[test.validator.clone]]
address = "2wDArvF5bAdLmmm4QZNVFJDrML6CCBbfbCeCLEb7c6QN"
[[test.validator.clone]]
address = "41SrrxMb1yzivSbUNjLShRV5yZf7S7YdQ1Emg2AxCviu"
2 changes: 1 addition & 1 deletion token-lending/LIQUIDITY_MINING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Users will still be able to claim rewards they accrued until this point.
#### Cancel

Cancelling a pool reward can be done by setting the end time to 0.
Note that only rewards longer than [solend_sdk::MIN_REWARD_PERIOD_SECS] can be cancelled.
Note that only rewards longer than `solend_sdk::MIN_REWARD_PERIOD_SECS` can be cancelled.
In this case we transfer tokens from the reward vault to the lending market reward token account.

#### Shorten
Expand Down
2 changes: 1 addition & 1 deletion token-lending/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "solend-program-cli"
version.workspace = true
version = "2.1.0"
authors = ["Solend Maintainers <[email protected]>"]
description = "Solend Program CLI"
edition = "2018"
Expand Down
5 changes: 5 additions & 0 deletions token-lending/cli/src/liquidity_mining.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! CLI commands related to liquidity mining.

mod migrate_all_reserves;

pub(crate) use migrate_all_reserves::command_upgrade_reserves_to_v2_1_0;
94 changes: 94 additions & 0 deletions token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! Temporary command that migrates all reserves to their new version.
//!
//! Delete once @v2.1.0 is fully deployed.
//!
//! Running this command before the upgrade:
//! > Found 1621 reserves to upgrade
//! > We'll spend ~54.46 $SOL on rent
//! > There are 87 reserves that were not used in the last 7 days as of 2025-05-08.

use solana_account_decoder::UiAccountEncoding;
use solana_account_decoder::UiDataSliceConfig;
use solana_client::rpc_config::RpcAccountInfoConfig;
use solana_client::rpc_config::RpcProgramAccountsConfig;
use solana_client::rpc_filter::RpcFilterType;
use solana_sdk::compute_budget::ComputeBudgetInstruction;
use solana_sdk::message::Message;
use solana_sdk::native_token::LAMPORTS_PER_SOL;
use solana_sdk::program_pack::Pack;
use solana_sdk::transaction::Transaction;
use solend_sdk::instruction::upgrade_reserve_to_v2_1_0;
use solend_sdk::state::Reserve;
use solend_sdk::state::RESERVE_LEN_V2_0_2;

use crate::send_transaction;
use crate::CommandResult;
use crate::Config;

/// How many reserves to upgrade in a single transaction.
///
/// We found the right value empirically.
const BATCH_SIZE: usize = 25;
/// How much to pay for compute units.
/// Helps lending txs.
const CU_PRICE: u64 = 3000;

/// Upgrades all reserves to the new version.
pub(crate) fn command_upgrade_reserves_to_v2_1_0(config: &mut Config) -> CommandResult {
let reserve_new_rent = config
.rpc_client
.get_minimum_balance_for_rent_exemption(Reserve::LEN)?;

Check warning on line 40 in token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs#L37-L40

Added lines #L37 - L40 were not covered by tests

// reserves before migration were sized to RESERVE_LEN_V2_0_2 and we're only interested in those
let filter = RpcProgramAccountsConfig {
filters: Some(vec![RpcFilterType::DataSize(RESERVE_LEN_V2_0_2 as _)]),
// with_context: Some(false),
account_config: RpcAccountInfoConfig {
data_slice: Some(UiDataSliceConfig {
offset: 1,
length: 8, // we don't need the data
}),
encoding: Some(UiAccountEncoding::Base64),
..Default::default()
},
..Default::default()
};
let reserves_to_upgrade = config
.rpc_client
.get_program_accounts_with_config(&config.lending_program_id, filter)?;

Check warning on line 58 in token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs#L43-L58

Added lines #L43 - L58 were not covered by tests

println!("Found {} reserves to upgrade", reserves_to_upgrade.len());

let missing_rent: u64 = reserves_to_upgrade
.iter()
.map(|(_, acc)| reserve_new_rent.saturating_sub(acc.lamports))
.sum();

println!(
"We'll spend ~{:.2} $SOL on rent",
missing_rent as f64 / LAMPORTS_PER_SOL as f64
);

Check warning on line 70 in token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs#L60-L70

Added lines #L60 - L70 were not covered by tests

for reserves in reserves_to_upgrade.chunks(BATCH_SIZE) {
let mut ixs = vec![ComputeBudgetInstruction::set_compute_unit_price(CU_PRICE)];
ixs.extend(reserves.iter().map(|(reserve_pubkey, _)| {
upgrade_reserve_to_v2_1_0(
config.lending_program_id,
*reserve_pubkey,
config.fee_payer.pubkey(),
)
}));

Check warning on line 80 in token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs#L72-L80

Added lines #L72 - L80 were not covered by tests

let recent_blockhash = config.rpc_client.get_latest_blockhash()?;

Check warning on line 82 in token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs#L82

Added line #L82 was not covered by tests

let message =
Message::new_with_blockhash(&ixs, Some(&config.fee_payer.pubkey()), &recent_blockhash);

let transaction =
Transaction::new(&vec![config.fee_payer.as_ref()], message, recent_blockhash);

send_transaction(config, transaction)?;

Check warning on line 90 in token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs#L84-L90

Added lines #L84 - L90 were not covered by tests
}

Ok(())
}

Check warning on line 94 in token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/liquidity_mining/migrate_all_reserves.rs#L93-L94

Added lines #L93 - L94 were not covered by tests
49 changes: 7 additions & 42 deletions token-lending/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
mod lending_state;
mod liquidity_mining;

use lending_state::SolendState;

use liquidity_mining::command_upgrade_reserves_to_v2_1_0;
use serde_json::Value;
use solana_account_decoder::UiAccountEncoding;
use solana_client::rpc_config::{RpcProgramAccountsConfig, RpcSendTransactionConfig};
Expand All @@ -11,7 +15,6 @@
instruction::set_lending_market_owner_and_config,
state::{validate_reserve_config, RateLimiterConfig},
};
use solend_sdk::instruction::upgrade_reserve_to_v2_1_0;
use solend_sdk::{
instruction::{
liquidate_obligation_and_redeem_reserve_collateral, redeem_reserve_collateral,
Expand All @@ -21,8 +24,6 @@
state::ReserveType,
};

mod lending_state;

use {
clap::{
crate_description, crate_name, crate_version, value_t, App, AppSettings, Arg, ArgMatches,
Expand Down Expand Up @@ -770,17 +771,8 @@
)
)
.subcommand(
SubCommand::with_name("upgrade-reserve")
.about("Migrate reserve to version 2.1.0")
.arg(
Arg::with_name("reserve")
.long("reserve")
.validator(is_pubkey)
.value_name("PUBKEY")
.takes_value(true)
.required(true)
.help("Reserve address"),
)
SubCommand::with_name("upgrade-all-reserves")
.about("Upgrade all reserves to version 2.1.0")

Check warning on line 775 in token-lending/cli/src/main.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/main.rs#L774-L775

Added lines #L774 - L775 were not covered by tests
)
.subcommand(
SubCommand::with_name("update-reserve")
Expand Down Expand Up @@ -1338,11 +1330,7 @@
risk_authority_pubkey,
)
}
("upgrade-reserve", Some(arg_matches)) => {
let reserve_pubkey = pubkey_of(arg_matches, "reserve").unwrap();

command_upgrade_reserve_to_v2_1_0(&mut config, reserve_pubkey)
}
("upgrade-all-reserves", _) => command_upgrade_reserves_to_v2_1_0(&mut config),

Check warning on line 1333 in token-lending/cli/src/main.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/cli/src/main.rs#L1333

Added line #L1333 was not covered by tests
("update-reserve", Some(arg_matches)) => {
let reserve_pubkey = pubkey_of(arg_matches, "reserve").unwrap();
let lending_market_owner_keypair =
Expand Down Expand Up @@ -1992,29 +1980,6 @@
Ok(())
}

fn command_upgrade_reserve_to_v2_1_0(config: &mut Config, reserve_pubkey: Pubkey) -> CommandResult {
let recent_blockhash = config.rpc_client.get_latest_blockhash()?;

let message = Message::new_with_blockhash(
&[
ComputeBudgetInstruction::set_compute_unit_price(30101),
upgrade_reserve_to_v2_1_0(
config.lending_program_id,
reserve_pubkey,
config.fee_payer.pubkey(),
),
],
Some(&config.fee_payer.pubkey()),
&recent_blockhash,
);

let transaction = Transaction::new(&vec![config.fee_payer.as_ref()], message, recent_blockhash);

send_transaction(config, transaction)?;

Ok(())
}

#[allow(clippy::too_many_arguments, clippy::unnecessary_unwrap)]
fn command_update_reserve(
config: &mut Config,
Expand Down
2 changes: 1 addition & 1 deletion token-lending/program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "solend-program"
version.workspace = true
version = "2.1.0"
description = "Solend Program"
authors = ["Solend Maintainers <[email protected]>"]
repository = "https://github.com/solendprotocol/solana-program-library"
Expand Down
2 changes: 1 addition & 1 deletion token-lending/sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "solend-sdk"
version.workspace = true
version = "2.1.0"
description = "Solend Sdk"
authors = ["Solend Maintainers <[email protected]>"]
repository = "https://github.com/solendprotocol/solana-program-library"
Expand Down
17 changes: 9 additions & 8 deletions token-lending/tests/liquidity-mining.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,21 @@ describe("liquidity mining", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());

const TEST_RESERVE_FOR_UPGRADE =
"BgxfHJDzm44T7XG68MYKx7YisTjZu73tVovyZSjJMpmw";

it("Upgrades reserve to 2.1.0 via CLI", async () => {
// There's an ix that upgrades a reserve to 2.1.0.
it("Upgrades reserves to 2.1.0 via CLI", async () => {
// There's an ix that upgrades all program reserves to 2.1.0.
// This ix is invocable via our CLI.
// In this test case for comfort and more test coverage we invoke the CLI
// command rather than crafting the ix ourselves.

// We check this reserve before & after the upgrade.
const SOME_TEST_RESERVE_TO_CHECK =
"BgxfHJDzm44T7XG68MYKx7YisTjZu73tVovyZSjJMpmw";

const rpcUrl = anchor.getProvider().connection.rpcEndpoint;

const reserveBefore = await anchor
.getProvider()
.connection.getAccountInfo(new PublicKey(TEST_RESERVE_FOR_UPGRADE));
.connection.getAccountInfo(new PublicKey(SOME_TEST_RESERVE_TO_CHECK));

expect(reserveBefore.data.length).to.eq(619); // old version data length
const expectedRentBefore = await anchor
Expand All @@ -36,7 +37,7 @@ describe("liquidity mining", () => {
// some reserves have more rent
expect(reserveBefore.lamports).to.be.greaterThanOrEqual(expectedRentBefore);

const command = `cargo run --quiet --bin solend-cli -- --url ${rpcUrl} upgrade-reserve --reserve ${TEST_RESERVE_FOR_UPGRADE}`;
const command = `cargo run --quiet --bin solend-cli -- --url ${rpcUrl} upgrade-all-reserves`;
console.log(`\$ ${command}`);
const cliProcess = exec(command);

Expand All @@ -58,7 +59,7 @@ describe("liquidity mining", () => {

const reserveAfter = await anchor
.getProvider()
.connection.getAccountInfo(new PublicKey(TEST_RESERVE_FOR_UPGRADE));
.connection.getAccountInfo(new PublicKey(SOME_TEST_RESERVE_TO_CHECK));

expect(reserveAfter.data.length).to.eq(5451); // new version data length
const expectedRentAfter = await anchor
Expand Down
Loading