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

Audit updates #208

Merged
merged 33 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2c60cde
Pool:initial-liquidation-fix
markuspluna Feb 6, 2024
fb4c073
Pool: moved HF checks inside health_factor.rs
markuspluna Feb 6, 2024
749905b
POOL: removed scalar_7 from liqudiation creation
markuspluna Feb 7, 2024
c4df04c
POOL: removed extra budget resets
markuspluna Feb 7, 2024
525a997
POOL: removed error parameters
markuspluna Feb 7, 2024
785ebb2
POOL: removed double negatives
markuspluna Feb 7, 2024
bd928be
POOL: divers scalar testing
markuspluna Feb 7, 2024
4acb549
Merge pull request #189 from blend-capital/update-liquidation-scalar-…
mootz12 Feb 7, 2024
8556fc0
factory: make deployment salt hash of admin and user provided salt
mootz12 Feb 22, 2024
8a04ebc
pool: patch 0 b_token mint inflation attack
mootz12 Feb 23, 2024
34d5e51
pool: remove token balance from interest calculation
mootz12 Feb 23, 2024
b9229fb
Merge pull request #203 from blend-capital/deployment-frontrunning
mootz12 Feb 26, 2024
58601a9
Merge pull request #204 from blend-capital/pool-slippage
mootz12 Feb 26, 2024
6785933
emitter: move dropped storage key to persistent storage (#192)
mootz12 Feb 26, 2024
5102917
pool: backstop: lazily evaluate default storage results (#193)
mootz12 Feb 26, 2024
a10f49d
pool: block self-liquidations (#195)
mootz12 Feb 26, 2024
0a88dad
pool: require_auth on init for pools deployed outside of factory (#197)
mootz12 Feb 26, 2024
9b9bfce
pool: block liquidation creation for the pool or backstop (#199)
mootz12 Feb 26, 2024
fca48c0
pool: block overwriting new queued reserves (#198)
mootz12 Feb 26, 2024
b87b545
pool: round health factor calculations up (#200)
mootz12 Feb 26, 2024
31b45c3
pool: round utilization up (#200)
mootz12 Feb 26, 2024
1d30893
pool: add max positions check (#201)
mootz12 Feb 26, 2024
bdeac71
backstop: minimize inflation attack exploit (#196)
mootz12 Feb 27, 2024
bc601ef
Merge pull request #205 from blend-capital/storage-fixes
mootz12 Feb 28, 2024
76e7bb2
Merge pull request #206 from blend-capital/pool-fixes
mootz12 Feb 28, 2024
dfc2a07
Merge pull request #207 from blend-capital/backstop-fixes
mootz12 Feb 28, 2024
123259b
pool: specify r_base in reserve config
mootz12 Feb 28, 2024
73245f5
pool: disable ir_mod updates for zero utilization
mootz12 Feb 28, 2024
91cf801
Merge pull request #209 from blend-capital/set_base_rate
mootz12 Mar 6, 2024
7a376b0
backstop: block user donations to prevent inflation attack vectors (#…
mootz12 Mar 7, 2024
ea26698
Merge pull request #210 from blend-capital/deposit-slippage
mootz12 Mar 7, 2024
48f8f86
backstop: optimize backstop threshold calculation (#211)
mootz12 Mar 7, 2024
943c8c7
Merge pull request #212 from blend-capital/optimize-threshold-check
mootz12 Mar 14, 2024
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
48 changes: 24 additions & 24 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ codegen-units = 1
lto = true

[workspace.dependencies.soroban-sdk]
version = "20.0.0"
version = "20.3.2"

[workspace.dependencies.soroban-fixed-point-math]
version = "1.0.0"
Expand Down
65 changes: 65 additions & 0 deletions backstop/src/backstop/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub fn execute_deposit(e: &Env, from: &Address, pool_address: &Address, amount:
backstop_token_client.transfer(from, &e.current_contract_address(), &amount);

let to_mint = pool_balance.convert_to_shares(amount);
if to_mint == 0 {
panic_with_error!(e, &BackstopError::InvalidShareMintAmount);
}
pool_balance.deposit(amount, to_mint);
user_balance.add_shares(to_mint);

Expand All @@ -35,6 +38,7 @@ mod tests {

use crate::{
backstop::execute_donate,
constants::SCALAR_7,
testutils::{create_backstop, create_backstop_token, create_mock_pool_factory},
};

Expand Down Expand Up @@ -208,4 +212,65 @@ mod tests {
execute_deposit(&e, &samwise, &pool_0_id, 100);
});
}

#[test]
#[should_panic(expected = "Error(Contract, #1005)")]
fn test_execute_deposit_zero_share_mint() {
let e = Env::default();
e.budget().reset_unlimited();
e.mock_all_auths_allowing_non_root_auth();

let backstop_address = create_backstop(&e);
let bombadil = Address::generate(&e);
let samwise = Address::generate(&e);
let frodo = Address::generate(&e);
let pool_0_id = Address::generate(&e);
let pool_1_id = Address::generate(&e);

let (_, backstop_token_client) = create_backstop_token(&e, &backstop_address, &bombadil);
backstop_token_client.mint(&samwise, &100_0000000);
backstop_token_client.mint(&frodo, &100_000_000_0000000);

let (_, mock_pool_factory_client) = create_mock_pool_factory(&e, &backstop_address);
mock_pool_factory_client.set_pool(&pool_0_id);
mock_pool_factory_client.set_pool(&pool_1_id);

// initialize pool 0 with funds + some profit
e.as_contract(&backstop_address, || {
execute_deposit(&e, &frodo, &pool_0_id, SCALAR_7);
execute_donate(&e, &frodo, &pool_0_id, 10_000_000 * SCALAR_7);
});

e.as_contract(&backstop_address, || {
execute_deposit(&e, &samwise, &pool_0_id, SCALAR_7);
});
}

// #[test]
// #[should_panic(expected = "Error(Contract, #1005)")]
// fn test_execute_deposit_small_initial_mint() {
// let e = Env::default();
// e.budget().reset_unlimited();
// e.mock_all_auths_allowing_non_root_auth();

// let backstop_address = create_backstop(&e);
// let bombadil = Address::generate(&e);
// let samwise = Address::generate(&e);
// let frodo = Address::generate(&e);
// let pool_0_id = Address::generate(&e);
// let pool_1_id = Address::generate(&e);

// let (_, backstop_token_client) = create_backstop_token(&e, &backstop_address, &bombadil);
// backstop_token_client.mint(&samwise, &100_0000000);
// backstop_token_client.mint(&frodo, &100_0000000);

// let (_, mock_pool_factory_client) = create_mock_pool_factory(&e, &backstop_address);
// mock_pool_factory_client.set_pool(&pool_0_id);
// mock_pool_factory_client.set_pool(&pool_1_id);

// e.as_contract(&backstop_address, || {
// execute_donate(&e, &frodo, &pool_0_id, SCALAR_7);
// execute_deposit(&e, &samwise, &pool_0_id, SCALAR_7 / 10 - 1);
// });
// }
}
2 changes: 2 additions & 0 deletions backstop/src/backstop/fund_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use soroban_sdk::{panic_with_error, unwrap::UnwrapOptimized, Address, Env};
use super::require_is_from_pool_factory;

/// Perform a draw from a pool's backstop
///
/// `pool_address` MUST be authenticated before calling
pub fn execute_draw(e: &Env, pool_address: &Address, amount: i128, to: &Address) {
require_nonnegative(e, amount);

Expand Down
20 changes: 4 additions & 16 deletions backstop/src/backstop/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,36 +57,26 @@ pub fn require_is_from_pool_factory(e: &Env, address: &Address, balance: i128) {
}
}

/// TODO: Duplicated from pool/pool/status.rs. Can this be moved to a common location?
///
/// Calculate the threshold for the pool's backstop balance
///
/// Returns true if the pool's backstop balance is above the threshold
/// NOTE: The calculation is the percentage^5 to simplify the calculation of the pools product constant.
/// Some useful calculation results:
/// - greater than 1 = 100+%
/// - 1_0000000 = 100%
/// - 0_0000100 = ~10%
/// - 0_0000003 = ~5%
/// - 0_0000000 = ~0-4%
pub fn require_pool_above_threshold(pool_backstop_data: &PoolBackstopData) -> bool {
// @dev: Calculation for pools product constant of underlying will often overflow i128
// so saturating mul is used. This is safe because the threshold is below i128::MAX and the
// protocol does not need to differentiate between pools over the threshold product constant.
// The calculation is:
// - Threshold % = (bal_blnd^4 * bal_usdc) / PC^5 such that PC is 200k
let threshold_pc = 320_000_000_000_000_000_000_000_000i128; // 3.2e26 (200k^5)
// floor balances to nearest full unit and calculate saturated pool product constant
// and scale to SCALAR_7 to get final division result in SCALAR_7 points

// floor balances to nearest full unit and calculate saturated pool product constant
let bal_blnd = pool_backstop_data.blnd / SCALAR_7;
let bal_usdc = pool_backstop_data.usdc / SCALAR_7;
let saturating_pool_pc = bal_blnd
.saturating_mul(bal_blnd)
.saturating_mul(bal_blnd)
.saturating_mul(bal_blnd)
.saturating_mul(bal_usdc)
.saturating_mul(SCALAR_7); // 10^7 * 10^7
saturating_pool_pc / threshold_pc >= SCALAR_7
.saturating_mul(bal_usdc);
saturating_pool_pc >= threshold_pc
}

/// The pool's backstop balances
Expand Down Expand Up @@ -134,8 +124,6 @@ impl PoolBalance {

/// Deposit tokens and shares into the pool
///
/// If this is the first time
///
/// ### Arguments
/// * `tokens` - The amount of tokens to add
/// * `shares` - The amount of shares to add
Expand Down
64 changes: 61 additions & 3 deletions backstop/src/backstop/withdrawal.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{contract::require_nonnegative, emissions, storage};
use crate::{contract::require_nonnegative, emissions, storage, BackstopError};
use sep_41_token::TokenClient;
use soroban_sdk::{unwrap::UnwrapOptimized, Address, Env};
use soroban_sdk::{panic_with_error, unwrap::UnwrapOptimized, Address, Env};

use super::Q4W;

Expand Down Expand Up @@ -56,6 +56,9 @@ pub fn execute_withdraw(e: &Env, from: &Address, pool_address: &Address, amount:
user_balance.dequeue_shares_for_withdrawal(e, amount, true);

let to_return = pool_balance.convert_to_tokens(amount);
if to_return == 0 {
panic_with_error!(e, &BackstopError::InvalidTokenWithdrawAmount);
}
pool_balance.withdraw(e, to_return, amount);

storage::set_user_balance(e, pool_address, from, &user_balance);
Expand All @@ -75,7 +78,7 @@ mod tests {
};

use crate::{
backstop::{execute_deposit, execute_donate},
backstop::{execute_deposit, execute_donate, execute_draw},
testutils::{
assert_eq_vec_q4w, create_backstop, create_backstop_token, create_mock_pool_factory,
},
Expand Down Expand Up @@ -363,6 +366,7 @@ mod tests {
assert_eq!(backstop_token_client.balance(&samwise), tokens);
});
}

#[test]
#[should_panic(expected = "Error(Contract, #8)")]
fn test_execute_withdrawal_negative_amount() {
Expand Down Expand Up @@ -413,4 +417,58 @@ mod tests {
execute_withdraw(&e, &samwise, &pool_address, -42_0000000);
});
}

#[test]
#[should_panic(expected = "Error(Contract, #1006)")]
fn test_execute_withdrawal_zero_tokens() {
let e = Env::default();
e.mock_all_auths_allowing_non_root_auth();

let backstop_address = create_backstop(&e);
let pool_address = Address::generate(&e);
let bombadil = Address::generate(&e);
let samwise = Address::generate(&e);
let frodo = Address::generate(&e);

let (_, backstop_token_client) = create_backstop_token(&e, &backstop_address, &bombadil);
backstop_token_client.mint(&samwise, &150_0000000);
backstop_token_client.mint(&frodo, &150_0000000);

let (_, mock_pool_factory_client) = create_mock_pool_factory(&e, &backstop_address);
mock_pool_factory_client.set_pool(&pool_address);

e.ledger().set(LedgerInfo {
protocol_version: 20,
sequence_number: 200,
timestamp: 10000,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 10,
min_persistent_entry_ttl: 10,
max_entry_ttl: 2000000,
});

// setup pool with queue for withdrawal and allow the backstop to incur a profit
e.as_contract(&backstop_address, || {
execute_deposit(&e, &frodo, &pool_address, 1_0000001);
execute_deposit(&e, &samwise, &pool_address, 1_0000000);
execute_queue_withdrawal(&e, &samwise, &pool_address, 1_0000000);
execute_draw(&e, &pool_address, 1_9999999, &frodo);
});

e.ledger().set(LedgerInfo {
protocol_version: 20,
sequence_number: 200,
timestamp: 10000 + 21 * 24 * 60 * 60 + 1,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 10,
min_persistent_entry_ttl: 10,
max_entry_ttl: 2000000,
});

e.as_contract(&backstop_address, || {
execute_withdraw(&e, &samwise, &pool_address, 1_0000000);
});
}
}
Loading
Loading