diff --git a/contracts/loan_pool/src/contract.rs b/contracts/loan_pool/src/contract.rs index 2993641..2c97d1f 100644 --- a/contracts/loan_pool/src/contract.rs +++ b/contracts/loan_pool/src/contract.rs @@ -1,19 +1,15 @@ use crate::dto::PoolState; use crate::error::LoanPoolError; use crate::interest::{self, get_interest}; -use crate::pool::Currency; -use crate::positions; -use crate::storage_types::PoolDataKey; -use crate::{pool, storage_types::Positions}; +use crate::storage::{Currency, Positions}; +use crate::{positions, storage}; -use soroban_sdk::{ - contract, contractimpl, contractmeta, symbol_short, token, Address, BytesN, Env, -}; +use soroban_sdk::{contract, contractimpl, contractmeta, token, Address, BytesN, Env}; // Metadata that is added on to the WASM custom section contractmeta!( key = "Desc", - val = "Lending pool with variable interest rate." + val = "Lending storage with variable interest rate." ); #[contract] @@ -22,26 +18,26 @@ struct LoanPoolContract; #[allow(dead_code)] #[contractimpl] impl LoanPoolContract { - /// Sets the currency of the pool and initializes its balance. + /// Sets the currency of the storage and initializes its balance. pub fn initialize( e: Env, loan_manager_addr: Address, currency: Currency, liquidation_threshold: i128, ) { - pool::write_loan_manager_addr(&e, loan_manager_addr); - pool::write_currency(&e, currency); - pool::write_liquidation_threshold(&e, liquidation_threshold); - pool::write_total_shares(&e, 0); - pool::write_total_balance(&e, 0); - pool::write_available_balance(&e, 0); - pool::write_accrual(&e, 10_000_000); // Default initial accrual value. - pool::write_accrual_last_updated(&e, e.ledger().timestamp()); - pool::change_interest_rate_multiplier(&e, 1); // Temporary parameter + storage::write_loan_manager_addr(&e, loan_manager_addr); + storage::write_currency(&e, currency); + storage::write_liquidation_threshold(&e, liquidation_threshold); + storage::write_total_shares(&e, 0); + storage::write_total_balance(&e, 0); + storage::write_available_balance(&e, 0); + storage::write_accrual(&e, 10_000_000); // Default initial accrual value. + storage::write_accrual_last_updated(&e, e.ledger().timestamp()); + storage::change_interest_rate_multiplier(&e, 1); // Temporary parameter } pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) -> Result<(), LoanPoolError> { - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); e.deployer().update_current_contract_wasm(new_wasm_hash); @@ -49,14 +45,14 @@ impl LoanPoolContract { } pub fn change_interest_rate_multiplier(e: Env, multiplier: i128) -> Result<(), LoanPoolError> { - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); - pool::change_interest_rate_multiplier(&e, multiplier); + storage::change_interest_rate_multiplier(&e, multiplier); Ok(()) } - /// Deposits token. Also, mints pool shares for the "user" Identifier. + /// Deposits token. Also, mints storage shares for the "user" Identifier. pub fn deposit(e: Env, user: Address, amount: i128) -> Result { user.require_auth(); if amount <= 0 { @@ -64,16 +60,16 @@ impl LoanPoolContract { } else { Self::add_interest_to_accrual(e.clone())?; - let token_address = pool::read_currency(&e)?.token_address; + let token_address = storage::read_currency(&e)?.token_address; let client = token::Client::new(&e, &token_address); client.transfer(&user, &e.current_contract_address(), &amount); - pool::change_available_balance(&e, amount)?; - pool::change_total_shares(&e, amount)?; - pool::change_total_balance(&e, amount)?; + storage::change_available_balance(&e, amount)?; + storage::change_total_shares(&e, amount)?; + storage::change_total_balance(&e, amount)?; - // Increase users position in pool as they deposit + // Increase users position in storage as they deposit // as this is deposit amount is added to receivables and // liabilities & collateral stays intact let liabilities: i128 = 0; // temp test param @@ -91,9 +87,7 @@ impl LoanPoolContract { Self::add_interest_to_accrual(e.clone())?; // Get users receivables - let Positions { - receivable_shares, .. - } = positions::read_positions(&e, &user); + let receivable_shares = storage::read_positions(&e, &user).receivable_shares; // Check that user is not trying to move more than receivables (TODO: also include collateral?) if amount > receivable_shares { @@ -112,15 +106,15 @@ impl LoanPoolContract { .checked_div(total_balance_tokens) .ok_or(LoanPoolError::OverOrUnderFlow)?; - let new_available_balance_tokens = pool::change_available_balance( + let new_available_balance_tokens = storage::change_available_balance( &e, amount.checked_neg().ok_or(LoanPoolError::OverOrUnderFlow)?, )?; - let new_total_balance_tokens = pool::change_total_balance( + let new_total_balance_tokens = storage::change_total_balance( &e, amount.checked_neg().ok_or(LoanPoolError::OverOrUnderFlow)?, )?; - let new_total_balance_shares = pool::change_total_shares(&e, shares_to_decrease)?; + let new_total_balance_shares = storage::change_total_shares(&e, shares_to_decrease)?; let liabilities: i128 = 0; let collateral: i128 = 0; positions::decrease_positions( @@ -131,72 +125,72 @@ impl LoanPoolContract { collateral, )?; - // Transfer tokens from pool to user - let token_address = &pool::read_currency(&e)?.token_address; + // Transfer tokens from storage to user + let token_address = &storage::read_currency(&e)?.token_address; let client = token::Client::new(&e, token_address); client.transfer(&e.current_contract_address(), &user, &amount); let new_annual_interest_rate = Self::get_interest(e.clone())?; - let pool_state = PoolState { + let storage_state = PoolState { total_balance_tokens: new_total_balance_tokens, available_balance_tokens: new_available_balance_tokens, total_balance_shares: new_total_balance_shares, annual_interest_rate: new_annual_interest_rate, }; - Ok(pool_state) + Ok(storage_state) } - /// Borrow tokens from the pool + /// Borrow tokens from the storage pub fn borrow(e: Env, user: Address, amount: i128) -> Result { /* Borrow should only be callable from the loans contract. This is as the loans contract will include the logic and checks that the borrowing can be actually done. Therefore we need to include a check that the caller is the loans contract. */ - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); user.require_auth(); Self::add_interest_to_accrual(e.clone())?; - let balance = pool::read_available_balance(&e)?; + let balance = storage::read_available_balance(&e)?; assert!( amount < balance, "Borrowed amount has to be less than available balance!" ); // Check that there is enough available balance - pool::change_available_balance( + storage::change_available_balance( &e, amount.checked_neg().ok_or(LoanPoolError::OverOrUnderFlow)?, )?; - // Increase users position in pool as they deposit + // Increase users position in storage as they deposit // as this is debt amount is added to liabilities and // collateral & receivables stays intact let collateral: i128 = 0; // temp test param let receivables: i128 = 0; // temp test param positions::increase_positions(&e, user.clone(), receivables, amount, collateral)?; - let token_address = &pool::read_currency(&e)?.token_address; + let token_address = &storage::read_currency(&e)?.token_address; let client = token::Client::new(&e, token_address); client.transfer(&e.current_contract_address(), &user, &amount); Ok(amount) } - /// Deposit tokens to the pool to be used as collateral + /// Deposit tokens to the storage to be used as collateral pub fn deposit_collateral(e: Env, user: Address, amount: i128) -> Result { user.require_auth(); assert!(amount > 0, "Amount must be positive!"); Self::add_interest_to_accrual(e.clone())?; - let token_address = &pool::read_currency(&e)?.token_address; + let token_address = &storage::read_currency(&e)?.token_address; let client = token::Client::new(&e, token_address); client.transfer(&user, &e.current_contract_address(), &amount); - // Increase users position in pool as they deposit + // Increase users position in storage as they deposit // as this is collateral amount is added to collateral and // liabilities & receivables stays intact let liabilities: i128 = 0; // temp test param @@ -210,15 +204,15 @@ impl LoanPoolContract { user.require_auth(); Self::add_interest_to_accrual(e.clone())?; - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); assert!(amount > 0, "Amount must be positive!"); - let token_address = &pool::read_currency(&e)?.token_address; + let token_address = &storage::read_currency(&e)?.token_address; let client = token::Client::new(&e, token_address); client.transfer(&e.current_contract_address(), &user, &amount); - // Increase users position in pool as they deposit + // Increase users position in storage as they deposit // as this is collateral amount is added to collateral and // liabilities & receivables stays intact let liabilities: i128 = 0; // temp test param @@ -233,8 +227,8 @@ impl LoanPoolContract { const SECONDS_IN_YEAR: u64 = 31_556_926; let current_timestamp = e.ledger().timestamp(); - let accrual = pool::read_accrual(&e)?; - let accrual_last_update = pool::read_accrual_last_updated(&e)?; + let accrual = storage::read_accrual(&e)?; + let accrual_last_update = storage::read_accrual_last_updated(&e)?; let ledgers_since_update = current_timestamp .checked_sub(accrual_last_update) .ok_or(LoanPoolError::OverOrUnderFlow)?; @@ -259,48 +253,39 @@ impl LoanPoolContract { .checked_add(interest_since_update) .ok_or(LoanPoolError::OverOrUnderFlow)?; - pool::write_accrual_last_updated(&e, current_timestamp); - e.events().publish( - (PoolDataKey::AccrualLastUpdate, symbol_short!("updated")), - current_timestamp, - ); - - pool::write_accrual(&e, new_accrual); - e.events().publish( - (PoolDataKey::Accrual, symbol_short!("updated")), - new_accrual, - ); + storage::write_accrual_last_updated(&e, current_timestamp); + storage::write_accrual(&e, new_accrual); Ok(()) } pub fn get_accrual(e: &Env) -> Result { - pool::read_accrual(e) + storage::read_accrual(e) } pub fn get_collateral_factor(e: &Env) -> Result { - pool::read_collateral_factor(e) + storage::read_collateral_factor(e) } - /// Get user's positions in the pool + /// Get user's positions in the storage pub fn get_user_positions(e: Env, user: Address) -> Positions { - positions::read_positions(&e, &user) + storage::read_positions(&e, &user) } /// Get contract data entries pub fn get_contract_balance(e: Env) -> Result { - pool::read_total_balance(&e) + storage::read_total_balance(&e) } pub fn get_total_balance_shares(e: Env) -> Result { - pool::read_total_shares(&e) + storage::read_total_shares(&e) } pub fn get_available_balance(e: Env) -> Result { - pool::read_available_balance(&e) + storage::read_available_balance(&e) } pub fn get_currency(e: Env) -> Result { - pool::read_currency(&e) + storage::read_currency(&e) } pub fn get_interest(e: Env) -> Result { @@ -309,26 +294,18 @@ impl LoanPoolContract { pub fn get_pool_state(e: Env) -> Result { Ok(PoolState { - total_balance_tokens: pool::read_total_balance(&e)?, - available_balance_tokens: pool::read_available_balance(&e)?, - total_balance_shares: pool::read_total_shares(&e)?, + total_balance_tokens: storage::read_total_balance(&e)?, + available_balance_tokens: storage::read_available_balance(&e)?, + total_balance_shares: storage::read_total_shares(&e)?, annual_interest_rate: interest::get_interest(e)?, }) } pub fn increase_liabilities(e: Env, user: Address, amount: i128) -> Result<(), LoanPoolError> { - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); positions::increase_positions(&e, user.clone(), 0, amount, 0)?; - e.events().publish( - ( - PoolDataKey::Positions(user), - symbol_short!("liab"), - symbol_short!("incr"), - ), - amount, - ); Ok(()) } @@ -338,7 +315,7 @@ impl LoanPoolContract { amount: i128, unpaid_interest: i128, ) -> Result<(), LoanPoolError> { - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); Self::add_interest_to_accrual(e.clone())?; @@ -349,17 +326,17 @@ impl LoanPoolContract { unpaid_interest / 10 }; - let amount_to_pool = amount + let amount_to_storage = amount .checked_sub(amount_to_admin) .ok_or(LoanPoolError::OverOrUnderFlow)?; - let client = token::Client::new(&e, &pool::read_currency(&e)?.token_address); - client.transfer(&user, &e.current_contract_address(), &amount_to_pool); + let client = token::Client::new(&e, &storage::read_currency(&e)?.token_address); + client.transfer(&user, &e.current_contract_address(), &amount_to_storage); client.transfer(&user, &loan_manager_addr, &amount_to_admin); positions::decrease_positions(&e, user, 0, amount, 0)?; - pool::change_available_balance(&e, amount - amount_to_admin)?; - pool::change_total_balance(&e, unpaid_interest - amount_to_admin)?; + storage::change_available_balance(&e, amount - amount_to_admin)?; + storage::change_total_balance(&e, unpaid_interest - amount_to_admin)?; Ok(()) } @@ -370,7 +347,7 @@ impl LoanPoolContract { max_allowed_amount: i128, unpaid_interest: i128, ) -> Result<(), LoanPoolError> { - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); Self::add_interest_to_accrual(e.clone())?; @@ -385,7 +362,7 @@ impl LoanPoolContract { .checked_sub(borrowed_amount) .ok_or(LoanPoolError::OverOrUnderFlow)?; - let client = token::Client::new(&e, &pool::read_currency(&e)?.token_address); + let client = token::Client::new(&e, &storage::read_currency(&e)?.token_address); client.transfer(&user, &e.current_contract_address(), &max_allowed_amount); client.transfer( &e.current_contract_address(), @@ -394,10 +371,10 @@ impl LoanPoolContract { ); client.transfer(&e.current_contract_address(), &user, &amount_to_user); - let user_liabilities = positions::read_positions(&e, &user).liabilities; + let user_liabilities = storage::read_positions(&e, &user).liabilities; positions::decrease_positions(&e, user, 0, user_liabilities, 0)?; - pool::change_available_balance(&e, borrowed_amount - amount_to_admin)?; - pool::change_total_balance(&e, unpaid_interest - amount_to_admin)?; + storage::change_available_balance(&e, borrowed_amount - amount_to_admin)?; + storage::change_total_balance(&e, unpaid_interest - amount_to_admin)?; Ok(()) } @@ -408,7 +385,7 @@ impl LoanPoolContract { unpaid_interest: i128, loan_owner: Address, ) -> Result<(), LoanPoolError> { - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); Self::add_interest_to_accrual(e.clone())?; @@ -419,17 +396,17 @@ impl LoanPoolContract { unpaid_interest / 10 }; - let amount_to_pool = amount + let amount_to_storage = amount .checked_sub(amount_to_admin) .ok_or(LoanPoolError::OverOrUnderFlow)?; - let client = token::Client::new(&e, &pool::read_currency(&e)?.token_address); - client.transfer(&user, &e.current_contract_address(), &amount_to_pool); + let client = token::Client::new(&e, &storage::read_currency(&e)?.token_address); + client.transfer(&user, &e.current_contract_address(), &amount_to_storage); client.transfer(&user, &loan_manager_addr, &amount_to_admin); positions::decrease_positions(&e, loan_owner, 0, amount, 0)?; - pool::change_available_balance(&e, amount)?; - pool::change_total_balance(&e, amount)?; + storage::change_available_balance(&e, amount)?; + storage::change_total_balance(&e, amount)?; Ok(()) } @@ -439,10 +416,10 @@ impl LoanPoolContract { amount_collateral: i128, loan_owner: Address, ) -> Result<(), LoanPoolError> { - let loan_manager_addr = pool::read_loan_manager_addr(&e)?; + let loan_manager_addr = storage::read_loan_manager_addr(&e)?; loan_manager_addr.require_auth(); - let client = token::Client::new(&e, &pool::read_currency(&e)?.token_address); + let client = token::Client::new(&e, &storage::read_currency(&e)?.token_address); client.transfer(&e.current_contract_address(), &user, &amount_collateral); positions::decrease_positions(&e, loan_owner, 0, 0, amount_collateral)?; @@ -726,7 +703,7 @@ mod test { let withdraw_result = contract_client.withdraw(&user, &amount); - assert_eq!(withdraw_result, contract_client.get_pool_state()); + assert_eq!(withdraw_result, contract_client.get_storage_state()); } #[test] fn add_accrual_full_usage() { diff --git a/contracts/loan_pool/src/interest.rs b/contracts/loan_pool/src/interest.rs index 6d21a85..149bbab 100644 --- a/contracts/loan_pool/src/interest.rs +++ b/contracts/loan_pool/src/interest.rs @@ -1,4 +1,4 @@ -use crate::{error::LoanPoolError, pool}; +use crate::{error::LoanPoolError, storage}; use soroban_sdk::Env; #[allow(dead_code)] @@ -8,13 +8,11 @@ pub const INTEREST_RATE_AT_PANIC: i128 = 1_000_000; // 10% pub const MAX_INTEREST_RATE: i128 = 3_000_000; // 30% pub const PANIC_BASE_RATE: i128 = -17_000_000; -#[allow(dead_code, unused_variables)] - pub fn get_interest(e: Env) -> Result { - let interest_rate_multiplier = pool::read_interest_rate_multiplier(&e)?; + let interest_rate_multiplier = storage::read_interest_rate_multiplier(&e)?; const PANIC_RATES_THRESHOLD: i128 = 90_000_000; - let available = pool::read_available_balance(&e)?; - let total = pool::read_total_balance(&e)?; + let available = storage::read_available_balance(&e)?; + let total = storage::read_total_balance(&e)?; if total > 0 { let slope_before_panic = (INTEREST_RATE_AT_PANIC diff --git a/contracts/loan_pool/src/lib.rs b/contracts/loan_pool/src/lib.rs index adc41b8..f1792d4 100644 --- a/contracts/loan_pool/src/lib.rs +++ b/contracts/loan_pool/src/lib.rs @@ -5,6 +5,5 @@ mod contract; mod dto; mod error; mod interest; -mod pool; mod positions; -mod storage_types; +mod storage; diff --git a/contracts/loan_pool/src/positions.rs b/contracts/loan_pool/src/positions.rs index 7eeb6fa..86a9d37 100644 --- a/contracts/loan_pool/src/positions.rs +++ b/contracts/loan_pool/src/positions.rs @@ -1,39 +1,5 @@ -use crate::{ - error::LoanPoolError, - storage_types::{extend_persistent, PoolDataKey, Positions}, -}; -use soroban_sdk::{Address, Env, IntoVal, Val}; - -pub fn read_positions(e: &Env, addr: &Address) -> Positions { - let key = PoolDataKey::Positions(addr.clone()); - // If positions exist, read them and return them as Val - if let Some(positions) = e.storage().persistent().get(&key) { - extend_persistent(e.clone(), &key); - positions - } else { - Positions { - receivable_shares: 0, - liabilities: 0, - collateral: 0, - } - } -} - -fn write_positions(e: &Env, addr: Address, receivables: i128, liabilities: i128, collateral: i128) { - let key: PoolDataKey = PoolDataKey::Positions(addr); - - let positions: Positions = Positions { - receivable_shares: receivables, - liabilities, - collateral, - }; - - let val: Val = positions.into_val(e); - - e.storage().persistent().set(&key, &val); - - extend_persistent(e.clone(), &key); -} +use crate::{error::LoanPoolError, storage}; +use soroban_sdk::{Address, Env}; pub fn increase_positions( e: &Env, @@ -42,12 +8,12 @@ pub fn increase_positions( liabilities: i128, collateral: i128, ) -> Result<(), LoanPoolError> { - let positions = read_positions(e, &addr); + let positions = storage::read_positions(e, &addr); let receivables_now: i128 = positions.receivable_shares; let liabilities_now: i128 = positions.liabilities; let collateral_now = positions.collateral; - write_positions( + storage::write_positions( e, addr, receivables_now @@ -70,7 +36,7 @@ pub fn decrease_positions( liabilities: i128, collateral: i128, ) -> Result<(), LoanPoolError> { - let positions = read_positions(e, &addr); + let positions = storage::read_positions(e, &addr); // TODO: Might need to use get rather than get_unchecked and convert from Option to V let receivables_now = positions.receivable_shares; @@ -86,7 +52,7 @@ pub fn decrease_positions( if collateral_now < collateral { panic!("insufficient collateral"); } - write_positions( + storage::write_positions( e, addr, receivables_now diff --git a/contracts/loan_pool/src/pool.rs b/contracts/loan_pool/src/storage.rs similarity index 62% rename from contracts/loan_pool/src/pool.rs rename to contracts/loan_pool/src/storage.rs index de9d1ae..e3ea475 100644 --- a/contracts/loan_pool/src/pool.rs +++ b/contracts/loan_pool/src/storage.rs @@ -1,20 +1,76 @@ -use crate::{ - error::LoanPoolError, - storage_types::{extend_persistent, PoolDataKey}, -}; use soroban_sdk::{contracttype, Address, Env, Symbol}; +use crate::error::LoanPoolError; + +/* Storage Types */ + +// Config for pool +#[derive(Clone)] +#[contracttype] +pub struct PoolConfig { + pub oracle: Address, // The contract address for the price oracle + pub status: u32, // Status of the pool +} + +#[derive(Clone)] +#[contracttype] +pub struct Positions { + // struct names under 9 characters are marginally more efficient. Need to think if we value marginal efficiency over readibility + pub receivable_shares: i128, + pub liabilities: i128, + pub collateral: i128, +} + #[contracttype] pub struct Currency { pub token_address: Address, pub ticker: Symbol, } +#[derive(Clone)] +#[contracttype] +enum PoolDataKey { + // Address of the loan manager for authorization. + LoanManagerAddress, + // Pool's token's address & ticker + Currency, + // The threshold when a loan should liquidate, unit is one-millionth + LiquidationThreshold, + // Users positions in the pool + Positions(Address), + // Total amount of shares in circulation + TotalBalanceShares, + // Total balance of pool + TotalBalanceTokens, + // Available balance of pool + AvailableBalanceTokens, + // Pool interest accrual index + Accrual, + // Last update ledger of accrual + AccrualLastUpdate, + // Interest rate multiplier + InterestRateMultiplier, +} + +/* Ledger Thresholds */ + +pub(crate) const DAY_IN_LEDGERS: u32 = 17280; // if ledger takes 5 seconds + +pub(crate) const POSITIONS_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS; +pub(crate) const POSITIONS_LIFETIME_THRESHOLD: u32 = POSITIONS_BUMP_AMOUNT - DAY_IN_LEDGERS; + +/* Persistent ttl bumper */ +fn extend_persistent(e: &Env, key: &PoolDataKey) { + e.storage() + .persistent() + .extend_ttl(key, POSITIONS_LIFETIME_THRESHOLD, POSITIONS_BUMP_AMOUNT); +} + pub fn write_loan_manager_addr(e: &Env, loan_manager_addr: Address) { let key = PoolDataKey::LoanManagerAddress; e.storage().persistent().set(&key, &loan_manager_addr); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); } pub fn read_loan_manager_addr(e: &Env) -> Result { @@ -31,7 +87,7 @@ pub fn write_currency(e: &Env, currency: Currency) { let key = PoolDataKey::Currency; e.storage().persistent().set(&key, ¤cy); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); } pub fn read_currency(e: &Env) -> Result { @@ -48,14 +104,14 @@ pub fn write_liquidation_threshold(e: &Env, threshold: i128) { let key = PoolDataKey::LiquidationThreshold; e.storage().persistent().set(&key, &threshold); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); } pub fn write_total_shares(e: &Env, amount: i128) { let key: PoolDataKey = PoolDataKey::TotalBalanceShares; e.storage().persistent().set(&key, &amount); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); } pub fn read_total_shares(e: &Env) -> Result { @@ -82,7 +138,7 @@ pub fn write_total_balance(e: &Env, amount: i128) { let key: PoolDataKey = PoolDataKey::TotalBalanceTokens; e.storage().persistent().set(&key, &amount); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); } pub fn read_total_balance(e: &Env) -> Result { @@ -109,7 +165,7 @@ pub fn write_available_balance(e: &Env, amount: i128) { let key: PoolDataKey = PoolDataKey::AvailableBalanceTokens; e.storage().persistent().set(&key, &amount); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); } pub fn read_available_balance(e: &Env) -> Result { @@ -136,24 +192,25 @@ pub fn write_accrual(e: &Env, accrual: i128) { let key = PoolDataKey::Accrual; e.storage().persistent().set(&key, &accrual); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); + + e.events().publish((key, "updated"), accrual); } pub fn read_accrual(e: &Env) -> Result { - let key = PoolDataKey::Accrual; - - if let Some(accrual) = e.storage().persistent().get(&key) { - accrual - } else { - Err(LoanPoolError::Accrual) - } + e.storage() + .persistent() + .get(&PoolDataKey::Accrual) + .ok_or(LoanPoolError::Accrual) } pub fn write_accrual_last_updated(e: &Env, sequence: u64) -> u64 { let key = PoolDataKey::AccrualLastUpdate; e.storage().persistent().set(&key, &sequence); - extend_persistent(e.clone(), &key); + extend_persistent(e, &key); + + e.events().publish((key, "updated"), e.ledger().timestamp()); sequence } @@ -187,3 +244,37 @@ pub fn read_collateral_factor(e: &Env) -> Result { .get(&PoolDataKey::LiquidationThreshold) .ok_or(LoanPoolError::LiquidationThreshold) } + +pub fn read_positions(e: &Env, addr: &Address) -> Positions { + let key = PoolDataKey::Positions(addr.clone()); + if let Some(positions) = e.storage().persistent().get(&key) { + extend_persistent(e, &key); + positions + } else { + Positions { + receivable_shares: 0, + liabilities: 0, + collateral: 0, + } + } +} + +pub fn write_positions( + e: &Env, + addr: Address, + receivables: i128, + liabilities: i128, + collateral: i128, +) { + let key = PoolDataKey::Positions(addr); + + let positions: Positions = Positions { + receivable_shares: receivables, + liabilities, + collateral, + }; + + e.storage().persistent().set(&key, &positions); + + extend_persistent(e, &key); +} diff --git a/contracts/loan_pool/src/storage_types.rs b/contracts/loan_pool/src/storage_types.rs deleted file mode 100644 index 1d60f65..0000000 --- a/contracts/loan_pool/src/storage_types.rs +++ /dev/null @@ -1,59 +0,0 @@ -use soroban_sdk::{contracttype, Address, Env}; - -/* Ledger Thresholds */ - -pub(crate) const DAY_IN_LEDGERS: u32 = 17280; // if ledger takes 5 seconds - -pub(crate) const POSITIONS_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS; -pub(crate) const POSITIONS_LIFETIME_THRESHOLD: u32 = POSITIONS_BUMP_AMOUNT - DAY_IN_LEDGERS; - -/* Storage Types */ - -// Config for pool -#[derive(Clone)] -#[contracttype] -pub struct PoolConfig { - pub oracle: Address, // The contract address for the price oracle - pub status: u32, // Status of the pool -} - -#[derive(Clone)] -#[contracttype] -pub struct Positions { - // struct names under 9 characters are marginally more efficient. Need to think if we value marginal efficiency over readibility - pub receivable_shares: i128, - pub liabilities: i128, - pub collateral: i128, -} - -#[derive(Clone)] -#[contracttype] -pub enum PoolDataKey { - // Address of the loan manager for authorization. - LoanManagerAddress, - // Pool's token's address & ticker - Currency, - // The threshold when a loan should liquidate, unit is one-millionth - LiquidationThreshold, - // Users positions in the pool - Positions(Address), - // Total amount of shares in circulation - TotalBalanceShares, - // Total balance of pool - TotalBalanceTokens, - // Available balance of pool - AvailableBalanceTokens, - // Pool interest accrual index - Accrual, - // Last update ledger of accrual - AccrualLastUpdate, - // Interest rate multiplier - InterestRateMultiplier, -} - -/* Persistent ttl bumper */ -pub fn extend_persistent(e: Env, key: &PoolDataKey) { - e.storage() - .persistent() - .extend_ttl(key, POSITIONS_LIFETIME_THRESHOLD, POSITIONS_BUMP_AMOUNT); -}