Skip to content

Commit a9613c3

Browse files
committed
fix: fixed withdraw calculations to include interest
1 parent 6b37bf7 commit a9613c3

File tree

6 files changed

+95
-67
lines changed

6 files changed

+95
-67
lines changed

contracts/loan_pool/src/contract.rs

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,39 +75,64 @@ impl LoanPoolContract {
7575
}
7676

7777
/// Transfers share tokens back, burns them and gives corresponding amount of tokens back to user. Returns amount of tokens withdrawn
78-
pub fn withdraw(e: Env, user: Address, amount: i128) -> Result<(i128, i128), Error> {
78+
pub fn withdraw(e: Env, user: Address, amount: i128) -> Result<PoolState, Error> {
7979
user.require_auth();
8080

8181
Self::add_interest_to_accrual(e.clone())?;
8282

8383
// Get users receivables
84-
let Positions { receivables, .. } = positions::read_positions(&e, &user);
84+
let Positions {
85+
receivable_shares, ..
86+
} = positions::read_positions(&e, &user);
8587

8688
// Check that user is not trying to move more than receivables (TODO: also include collateral?)
87-
assert!(
88-
amount <= receivables,
89-
"Amount can not be greater than receivables!"
90-
);
91-
let available_balance = Self::get_available_balance(e.clone())?;
92-
assert!(amount <= available_balance);
89+
if amount > receivable_shares {
90+
return Err(Error::WithdrawIsNegative);
91+
}
9392

94-
// TODO: Decrease AvailableBalance
95-
pool::change_available_balance(&e, amount.checked_neg().ok_or(Error::OverOrUnderFlow)?)?;
96-
// TODO: Decrease TotalShares - Positions should have shares if we use them
97-
// TODO: Decrease TotalBalance
98-
pool::change_total_balance(&e, amount.checked_neg().ok_or(Error::OverOrUnderFlow)?)?;
93+
let available_balance_tokens = Self::get_available_balance(e.clone())?;
94+
if amount > available_balance_tokens {
95+
return Err(Error::WithdrawOverBalance);
96+
}
97+
let total_balance_shares = Self::get_total_balance_shares(e.clone())?;
98+
let total_balance_tokens = Self::get_contract_balance(e.clone())?;
99+
let shares_to_decrease = amount
100+
.checked_mul(total_balance_shares)
101+
.ok_or(Error::OverOrUnderFlow)?
102+
.checked_div(total_balance_tokens)
103+
.ok_or(Error::OverOrUnderFlow)?;
99104

100-
// Decrease users position in pool as they withdraw
105+
let new_available_balance_tokens = pool::change_available_balance(
106+
&e,
107+
amount.checked_neg().ok_or(Error::OverOrUnderFlow)?,
108+
)?;
109+
let new_total_balance_tokens =
110+
pool::change_total_balance(&e, amount.checked_neg().ok_or(Error::OverOrUnderFlow)?)?;
111+
let new_total_balance_shares = pool::change_total_shares(&e, shares_to_decrease)?;
101112
let liabilities: i128 = 0;
102113
let collateral: i128 = 0;
103-
positions::decrease_positions(&e, user.clone(), amount, liabilities, collateral)?;
114+
positions::decrease_positions(
115+
&e,
116+
user.clone(),
117+
shares_to_decrease,
118+
liabilities,
119+
collateral,
120+
)?;
104121

105122
// Transfer tokens from pool to user
106123
let token_address = &pool::read_currency(&e)?.token_address;
107124
let client = token::Client::new(&e, token_address);
108125
client.transfer(&e.current_contract_address(), &user, &amount);
109126

110-
Ok((amount, amount))
127+
let new_annual_interest_rate = Self::get_interest(e.clone())?;
128+
129+
let pool_state = PoolState {
130+
total_balance_tokens: new_total_balance_tokens,
131+
available_balance_tokens: new_available_balance_tokens,
132+
total_balance_shares: new_total_balance_shares,
133+
annual_interest_rate: new_annual_interest_rate,
134+
};
135+
Ok(pool_state)
111136
}
112137

113138
/// Borrow tokens from the pool
@@ -247,6 +272,10 @@ impl LoanPoolContract {
247272
pool::read_total_balance(&e)
248273
}
249274

275+
pub fn get_total_balance_shares(e: Env) -> Result<i128, Error> {
276+
pool::read_total_shares(&e)
277+
}
278+
250279
pub fn get_available_balance(e: Env) -> Result<i128, Error> {
251280
pool::read_available_balance(&e)
252281
}
@@ -261,9 +290,9 @@ impl LoanPoolContract {
261290

262291
pub fn get_pool_state(e: Env) -> Result<PoolState, Error> {
263292
Ok(PoolState {
264-
total_balance: pool::read_total_balance(&e)?,
265-
available_balance: pool::read_available_balance(&e)?,
266-
total_shares: pool::read_total_shares(&e)?,
293+
total_balance_tokens: pool::read_total_balance(&e)?,
294+
available_balance_tokens: pool::read_available_balance(&e)?,
295+
total_balance_shares: pool::read_total_shares(&e)?,
267296
annual_interest_rate: interest::get_interest(e)?,
268297
})
269298
}
@@ -529,7 +558,7 @@ mod test {
529558
}
530559

531560
#[test]
532-
#[should_panic(expected = "Amount can not be greater than receivables!")]
561+
#[should_panic(expected = "Error(Contract, #12)")]
533562
fn withdraw_more_than_balance() {
534563
let e = Env::default();
535564
e.mock_all_auths();
@@ -597,9 +626,9 @@ mod test {
597626

598627
contract_client.borrow(&user2, &500);
599628

600-
let withdraw_result: (i128, i128) = contract_client.withdraw(&user, &amount);
629+
let withdraw_result = contract_client.withdraw(&user, &amount);
601630

602-
assert_eq!(withdraw_result, (amount, amount));
631+
assert_eq!(withdraw_result, contract_client.get_pool_state());
603632
}
604633
#[test]
605634
fn add_accrual_full_usage() {

contracts/loan_pool/src/dto.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use soroban_sdk::contracttype;
22

33
#[contracttype]
4+
#[derive(Debug, PartialEq)]
45
pub struct PoolState {
5-
pub total_balance: i128,
6-
pub available_balance: i128,
7-
pub total_shares: i128,
6+
pub total_balance_tokens: i128,
7+
pub available_balance_tokens: i128,
8+
pub total_balance_shares: i128,
89
pub annual_interest_rate: i128,
910
}

contracts/loan_pool/src/pool.rs

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pub enum Error {
2121
AccrualLastUpdated = 8,
2222
OverOrUnderFlow = 9,
2323
NegativeDeposit = 10,
24+
WithdrawOverBalance = 11,
25+
WithdrawIsNegative = 12,
2426
}
2527

2628
pub fn write_loan_manager_addr(e: &Env, loan_manager_addr: Address) {
@@ -65,14 +67,14 @@ pub fn write_liquidation_threshold(e: &Env, threshold: i128) {
6567
}
6668

6769
pub fn write_total_shares(e: &Env, amount: i128) {
68-
let key: PoolDataKey = PoolDataKey::TotalShares;
70+
let key: PoolDataKey = PoolDataKey::TotalBalanceShares;
6971

7072
e.storage().persistent().set(&key, &amount);
7173
extend_persistent(e.clone(), &key);
7274
}
7375

7476
pub fn read_total_shares(e: &Env) -> Result<i128, Error> {
75-
let key: PoolDataKey = PoolDataKey::TotalBalance;
77+
let key: PoolDataKey = PoolDataKey::TotalBalanceTokens;
7678

7779
if let Some(total_shares) = e.storage().persistent().get(&key) {
7880
total_shares
@@ -81,27 +83,25 @@ pub fn read_total_shares(e: &Env) -> Result<i128, Error> {
8183
}
8284
}
8385

84-
pub fn change_total_shares(e: &Env, amount: i128) -> Result<(), Error> {
86+
pub fn change_total_shares(e: &Env, amount: i128) -> Result<i128, Error> {
8587
let current_balance = read_total_shares(e)?;
8688

87-
write_total_shares(
88-
e,
89-
amount
90-
.checked_add(current_balance)
91-
.ok_or(Error::OverOrUnderFlow)?,
92-
);
93-
Ok(())
89+
let new_amount = amount
90+
.checked_add(current_balance)
91+
.ok_or(Error::OverOrUnderFlow)?;
92+
write_total_shares(e, new_amount);
93+
Ok(new_amount)
9494
}
9595

9696
pub fn write_total_balance(e: &Env, amount: i128) {
97-
let key: PoolDataKey = PoolDataKey::TotalBalance;
97+
let key: PoolDataKey = PoolDataKey::TotalBalanceTokens;
9898

9999
e.storage().persistent().set(&key, &amount);
100100
extend_persistent(e.clone(), &key);
101101
}
102102

103103
pub fn read_total_balance(e: &Env) -> Result<i128, Error> {
104-
let key: PoolDataKey = PoolDataKey::TotalBalance;
104+
let key: PoolDataKey = PoolDataKey::TotalBalanceTokens;
105105

106106
if let Some(total_balance) = e.storage().persistent().get(&key) {
107107
total_balance
@@ -110,27 +110,25 @@ pub fn read_total_balance(e: &Env) -> Result<i128, Error> {
110110
}
111111
}
112112

113-
pub fn change_total_balance(e: &Env, amount: i128) -> Result<(), Error> {
113+
pub fn change_total_balance(e: &Env, amount: i128) -> Result<i128, Error> {
114114
let current_balance = read_total_balance(e)?;
115115

116-
write_total_balance(
117-
e,
118-
amount
119-
.checked_add(current_balance)
120-
.ok_or(Error::OverOrUnderFlow)?,
121-
);
122-
Ok(())
116+
let new_amount = amount
117+
.checked_add(current_balance)
118+
.ok_or(Error::OverOrUnderFlow)?;
119+
write_total_balance(e, new_amount);
120+
Ok(new_amount)
123121
}
124122

125123
pub fn write_available_balance(e: &Env, amount: i128) {
126-
let key: PoolDataKey = PoolDataKey::AvailableBalance;
124+
let key: PoolDataKey = PoolDataKey::AvailableBalanceTokens;
127125

128126
e.storage().persistent().set(&key, &amount);
129127
extend_persistent(e.clone(), &key);
130128
}
131129

132130
pub fn read_available_balance(e: &Env) -> Result<i128, Error> {
133-
let key: PoolDataKey = PoolDataKey::AvailableBalance;
131+
let key: PoolDataKey = PoolDataKey::AvailableBalanceTokens;
134132

135133
if let Some(available_balance) = e.storage().persistent().get(&key) {
136134
available_balance
@@ -139,16 +137,14 @@ pub fn read_available_balance(e: &Env) -> Result<i128, Error> {
139137
}
140138
}
141139

142-
pub fn change_available_balance(e: &Env, amount: i128) -> Result<(), Error> {
140+
pub fn change_available_balance(e: &Env, amount: i128) -> Result<i128, Error> {
143141
let current_balance = read_available_balance(e)?;
144142

145-
write_available_balance(
146-
e,
147-
amount
148-
.checked_add(current_balance)
149-
.ok_or(Error::OverOrUnderFlow)?,
150-
);
151-
Ok(())
143+
let new_amount = amount
144+
.checked_add(current_balance)
145+
.ok_or(Error::OverOrUnderFlow)?;
146+
write_available_balance(e, new_amount);
147+
Ok(new_amount)
152148
}
153149

154150
pub fn write_accrual(e: &Env, accrual: i128) {
@@ -168,11 +164,13 @@ pub fn read_accrual(e: &Env) -> Result<i128, Error> {
168164
}
169165
}
170166

171-
pub fn write_accrual_last_updated(e: &Env, sequence: u64) {
167+
pub fn write_accrual_last_updated(e: &Env, sequence: u64) -> u64 {
172168
let key = PoolDataKey::AccrualLastUpdate;
173169

174170
e.storage().persistent().set(&key, &sequence);
175171
extend_persistent(e.clone(), &key);
172+
173+
sequence
176174
}
177175

178176
pub fn read_accrual_last_updated(e: &Env) -> Result<u64, Error> {

contracts/loan_pool/src/positions.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub fn read_positions(e: &Env, addr: &Address) -> Positions {
1010
positions
1111
} else {
1212
Positions {
13-
receivables: 0,
13+
receivable_shares: 0,
1414
liabilities: 0,
1515
collateral: 0,
1616
}
@@ -21,7 +21,7 @@ fn write_positions(e: &Env, addr: Address, receivables: i128, liabilities: i128,
2121
let key: PoolDataKey = PoolDataKey::Positions(addr);
2222

2323
let positions: Positions = Positions {
24-
receivables,
24+
receivable_shares: receivables,
2525
liabilities,
2626
collateral,
2727
};
@@ -42,7 +42,7 @@ pub fn increase_positions(
4242
) -> Result<(), Error> {
4343
let positions = read_positions(e, &addr);
4444

45-
let receivables_now: i128 = positions.receivables;
45+
let receivables_now: i128 = positions.receivable_shares;
4646
let liabilities_now: i128 = positions.liabilities;
4747
let collateral_now = positions.collateral;
4848
write_positions(
@@ -71,7 +71,7 @@ pub fn decrease_positions(
7171
let positions = read_positions(e, &addr);
7272

7373
// TODO: Might need to use get rather than get_unchecked and convert from Option<V> to V
74-
let receivables_now = positions.receivables;
74+
let receivables_now = positions.receivable_shares;
7575
let liabilities_now = positions.liabilities;
7676
let collateral_now = positions.collateral;
7777

contracts/loan_pool/src/storage_types.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub struct PoolConfig {
2121
#[contracttype]
2222
pub struct Positions {
2323
// struct names under 9 characters are marginally more efficient. Need to think if we value marginal efficiency over readibility
24-
pub receivables: i128,
24+
pub receivable_shares: i128,
2525
pub liabilities: i128,
2626
pub collateral: i128,
2727
}
@@ -38,11 +38,11 @@ pub enum PoolDataKey {
3838
// Users positions in the pool
3939
Positions(Address),
4040
// Total amount of shares in circulation
41-
TotalShares,
41+
TotalBalanceShares,
4242
// Total balance of pool
43-
TotalBalance,
43+
TotalBalanceTokens,
4444
// Available balance of pool
45-
AvailableBalance,
45+
AvailableBalanceTokens,
4646
// Pool interest accrual index
4747
Accrual,
4848
// Last update ledger of accrual

src/contexts/pool-context.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ const fetchPoolState = async (ticker: SupportedCurrency): Promise<PoolState> =>
5858
if (result.isOk()) {
5959
const value = result.unwrap();
6060
return {
61-
totalBalance: value.total_balance,
62-
availableBalance: value.available_balance,
63-
totalShares: value.total_shares,
61+
totalBalance: value.total_balance_tokens,
62+
availableBalance: value.available_balance_tokens,
63+
totalShares: value.total_balance_shares,
6464
annualInterestRate: value.annual_interest_rate,
6565
};
6666
}

0 commit comments

Comments
 (0)