Bank: Close zero position when withdrawing 0 (#793)

This commit is contained in:
Christian Kamm 2023-11-22 15:54:38 +01:00 committed by GitHub
parent 57a9835aa8
commit eeb172e44e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 111 additions and 82 deletions

View File

@ -594,7 +594,7 @@ impl Bank {
require_gte!(native_amount, 0);
let native_position = position.native(self);
if native_position.is_positive() {
if !native_position.is_negative() {
let new_native_position = native_position - native_amount;
if !new_native_position.is_negative() {
// withdraw deposits only
@ -1022,9 +1022,108 @@ mod tests {
use super::*;
#[test]
pub fn change() -> Result<()> {
fn bank_change_runner(start: f64, change: i32, is_in_use: bool, use_withdraw: bool) {
println!(
"testing: in use: {is_in_use}, start: {start}, change: {change}, use_withdraw: {use_withdraw}",
);
let epsilon = I80F48::from_bits(1);
//
// SETUP
//
let mut bank = Bank::zeroed();
bank.net_borrow_limit_window_size_ts = 1; // dummy
bank.net_borrow_limit_per_window_quote = i64::MAX; // max since we don't want this to interfere
bank.deposit_index = I80F48::from_num(100.0);
bank.borrow_index = I80F48::from_num(10.0);
bank.loan_origination_fee_rate = I80F48::from_num(0.1);
let indexed = |v: I80F48, b: &Bank| {
if v > 0 {
let i = v / b.deposit_index;
if i * b.deposit_index < v {
i + I80F48::DELTA
} else {
i
}
} else {
v / b.borrow_index
}
};
let mut account = TokenPosition {
indexed_position: I80F48::ZERO,
token_index: 0,
in_use_count: u16::from(is_in_use),
cumulative_deposit_interest: 0.0,
cumulative_borrow_interest: 0.0,
previous_index: I80F48::ZERO,
padding: Default::default(),
reserved: [0; 128],
};
account.indexed_position = indexed(I80F48::from_num(start), &bank);
if start >= 0.0 {
bank.indexed_deposits = account.indexed_position;
} else {
bank.indexed_borrows = -account.indexed_position;
}
// get the rounded start value
let start_native = account.native(&bank);
//
// TEST
//
let change = I80F48::from(change);
let dummy_now_ts = 1 as u64;
let dummy_price = I80F48::ZERO;
let is_active = if use_withdraw {
bank.withdraw_with_fee(&mut account, change, dummy_now_ts)
.unwrap()
.position_is_active
} else {
bank.change_with_fee(&mut account, change, dummy_now_ts)
.unwrap()
.position_is_active
};
let mut expected_native = start_native + change;
let is_deposit_into_nonnegative = start >= 0.0 && change >= 0 && !use_withdraw;
if expected_native >= 0.0
&& expected_native < 1.0
&& !is_in_use
&& !is_deposit_into_nonnegative
{
assert!(!is_active);
assert_eq!(bank.dust, expected_native);
expected_native = I80F48::ZERO;
} else {
assert!(is_active);
assert_eq!(bank.dust, I80F48::ZERO);
}
if change < 0 && expected_native < 0 {
let new_borrow = -(expected_native - min(start_native, I80F48::ZERO));
expected_native -= new_borrow * bank.loan_origination_fee_rate;
}
let expected_indexed = indexed(expected_native, &bank);
// at most one epsilon error in the resulting indexed value
assert!((account.indexed_position - expected_indexed).abs() <= epsilon);
if account.indexed_position.is_positive() {
assert_eq!(bank.indexed_deposits, account.indexed_position);
assert_eq!(bank.indexed_borrows, I80F48::ZERO);
} else {
assert_eq!(bank.indexed_deposits, I80F48::ZERO);
assert_eq!(bank.indexed_borrows, -account.indexed_position);
}
}
#[test]
pub fn bank_change() -> Result<()> {
let cases = [
(-10.1, 1),
(-10.1, 10),
@ -1048,89 +1147,19 @@ mod tests {
(0.0, -1),
(-0.1, -1),
(-1.1, -10),
(10.0, 0),
(1.0, 0),
(0.1, 0),
(0.0, 0),
(-0.1, 0),
];
for is_in_use in [false, true] {
for (start, change) in cases {
println!(
"testing: in use: {}, start: {}, change: {}",
is_in_use, start, change
);
//
// SETUP
//
let mut bank = Bank::zeroed();
bank.net_borrow_limit_window_size_ts = 1; // dummy
bank.net_borrow_limit_per_window_quote = i64::MAX; // max since we don't want this to interfere
bank.deposit_index = I80F48::from_num(100.0);
bank.borrow_index = I80F48::from_num(10.0);
bank.loan_origination_fee_rate = I80F48::from_num(0.1);
let indexed = |v: I80F48, b: &Bank| {
if v > 0 {
v / b.deposit_index
} else {
v / b.borrow_index
}
};
let mut account = TokenPosition {
indexed_position: I80F48::ZERO,
token_index: 0,
in_use_count: u16::from(is_in_use),
cumulative_deposit_interest: 0.0,
cumulative_borrow_interest: 0.0,
previous_index: I80F48::ZERO,
padding: Default::default(),
reserved: [0; 128],
};
account.indexed_position = indexed(I80F48::from_num(start), &bank);
if start >= 0.0 {
bank.indexed_deposits = account.indexed_position;
} else {
bank.indexed_borrows = -account.indexed_position;
}
// get the rounded start value
let start_native = account.native(&bank);
//
// TEST
//
let change = I80F48::from(change);
let dummy_now_ts = 1 as u64;
let dummy_price = I80F48::ZERO;
let is_active = bank
.change_with_fee(&mut account, change, dummy_now_ts)?
.position_is_active;
let mut expected_native = start_native + change;
if expected_native >= 0.0 && expected_native < 1.0 && !is_in_use {
assert!(!is_active);
assert_eq!(bank.dust, expected_native);
expected_native = I80F48::ZERO;
} else {
assert!(is_active);
assert_eq!(bank.dust, I80F48::ZERO);
}
if change < 0 && expected_native < 0 {
let new_borrow = -(expected_native - min(start_native, I80F48::ZERO));
expected_native -= new_borrow * bank.loan_origination_fee_rate;
}
let expected_indexed = indexed(expected_native, &bank);
// at most one epsilon error in the resulting indexed value
assert!((account.indexed_position - expected_indexed).abs() <= epsilon);
if account.indexed_position.is_positive() {
assert_eq!(bank.indexed_deposits, account.indexed_position);
assert_eq!(bank.indexed_borrows, I80F48::ZERO);
} else {
assert_eq!(bank.indexed_deposits, I80F48::ZERO);
assert_eq!(bank.indexed_borrows, -account.indexed_position);
bank_change_runner(start, change, is_in_use, false);
if change == 0 {
// check withdrawing 0
bank_change_runner(start, change, is_in_use, true);
}
}
}