Skip to content

Commit 75b33f5

Browse files
authored
Merge pull request #35 from dubemoyibe-star/feat/lightweight-loan-status-view
Implemeted lightweight loan-status view
2 parents 30a42b2 + 41dd720 commit 75b33f5

3 files changed

Lines changed: 376 additions & 45 deletions

File tree

README.md

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -181,45 +181,65 @@ pub fn unlock_nft(env: Env, nft_id: u64)
181181
**Key Functions**:
182182
```rust
183183
// Initialize the contract
184-
pub fn initialize(env: Env, admin: Address, pool_address: Address)
184+
pub fn initialize(
185+
env: Env,
186+
nft_contract: Address,
187+
lending_pool: Address,
188+
token: Address,
189+
admin: Address
190+
)
185191

186192
// Request a loan
187-
pub fn request_loan(
188-
env: Env,
189-
borrower: Address,
190-
nft_id: u64,
191-
amount: i128
192-
) -> u64
193+
pub fn request_loan(env: Env, borrower: Address, amount: i128, term: u32) -> u32
193194

194-
// Approve a loan
195-
pub fn approve_loan(env: Env, loan_id: u64)
195+
// Approve a loan (admin only)
196+
pub fn approve_loan(env: Env, loan_id: u32)
196197

197198
// Repay loan
198-
pub fn repay_loan(env: Env, loan_id: u64, amount: i128)
199+
pub fn repay(env: Env, borrower: Address, loan_id: u32, amount: i128)
200+
201+
// Cancel pending loan (borrower only)
202+
pub fn cancel_loan(env: Env, borrower: Address, loan_id: u32)
203+
204+
// Reject pending loan (admin only)
205+
pub fn reject_loan(env: Env, loan_id: u32, reason: String)
199206

200-
// Get loan details
201-
pub fn get_loan(env: Env, loan_id: u64) -> Loan
207+
// Get full loan details (triggers accrual)
208+
pub fn get_loan(env: Env, loan_id: u32) -> Result<Loan, LoanError>
202209

203-
// Check loan status
204-
pub fn get_loan_status(env: Env, loan_id: u64) -> LoanStatus
210+
// Get loan status only (no accrual) - lightweight view for indexers
211+
pub fn get_loan_status(env: Env, loan_id: u32) -> Result<LoanStatus, LoanError>
212+
213+
// Get all loan IDs for a borrower
214+
pub fn get_borrower_loans(env: Env, borrower: Address) -> Vec<u32>
215+
216+
// Get loan IDs for a borrower filtered by status (no accrual)
217+
pub fn get_borrower_loans_by_status(env: Env, borrower: Address, status: LoanStatus) -> Vec<u32>
205218
```
206219

207220
**Loan States**:
208221
```rust
209222
pub enum LoanStatus {
210-
Requested, // Loan requested, awaiting approval
223+
Pending, // Loan requested, awaiting approval
211224
Approved, // Approved, funds disbursed
212-
Active, // Repayment in progress
213225
Repaid, // Fully repaid
214-
Defaulted, // Payment missed
226+
Defaulted, // Payment missed, collateral seized
227+
Cancelled, // Cancelled by borrower before approval
228+
Rejected, // Rejected by admin
229+
Liquidated, // Liquidated by liquidator
215230
}
216231
```
217232

233+
**View Functions**:
234+
- `get_loan_status(loan_id)` - Returns only the status enum without running accrual calculations. Use this for lightweight queries when you only need to know loan state.
235+
- `get_borrower_loans_by_status(borrower, status)` - Returns loan IDs filtered by status. Useful for indexers to query specific loan states without fetching full loan data.
236+
218237
**Business Logic**:
219-
- Minimum credit score: 600
220-
- Maximum loan-to-value: 80%
221-
- Interest rate: Based on credit score
222-
- Repayment period: Configurable
238+
- Minimum credit score: 500 (configurable via `set_min_score`)
239+
- Maximum loans per borrower: 3 (configurable via `set_max_loans_per_borrower`)
240+
- Interest rate: Oracle-based or configurable default (1200 BPS)
241+
- Late fee rate: Configurable (500 BPS default)
242+
- Maximum extensions: 3 (configurable by calling `extend_loan`)
223243

224244
**Tests**:
225245
- ✅ Loan request flow
@@ -228,6 +248,8 @@ pub enum LoanStatus {
228248
- ✅ Low score rejection
229249
- ✅ Unauthorized repayment prevention
230250
- ✅ Access controls
251+
- ✅ get_loan_status returns correct status without accrual
252+
- ✅ get_borrower_loans_by_status filters correctly
231253

232254
### 3. Lending Pool Contract
233255

loan_manager/src/lib.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,18 +1212,19 @@ impl LoanManager {
12121212
Ok(loan)
12131213
}
12141214

1215-
/// Returns the exact current total debt (principal + accrued interest + accrued late fee) for a loan.
1216-
/// This amount matches exactly what `repay` would charge at the current ledger.
1217-
pub fn quote_total_debt(env: Env, loan_id: u32) -> Result<i128, LoanError> {
1215+
/// Returns the loan status without triggering interest/late fee accrual.
1216+
/// This is a lightweight view function for indexers and frontend queries
1217+
/// that only need to know the current state of a loan.
1218+
pub fn get_loan_status(env: Env, loan_id: u32) -> Result<LoanStatus, LoanError> {
12181219
let loan_key = DataKey::Loan(loan_id);
1219-
let mut loan: Loan = env
1220+
// Read loan directly without mutating reference to avoid accrual
1221+
let loan: Loan = env
12201222
.storage()
12211223
.persistent()
12221224
.get(&loan_key)
12231225
.ok_or(LoanError::LoanNotFound)?;
12241226
Self::bump_persistent_ttl(&env, &loan_key);
1225-
let (total_debt, _) = Self::current_total_debt(&env, &mut loan)?;
1226-
Ok(total_debt)
1227+
Ok(loan.status)
12271228
}
12281229

12291230
pub fn repay(env: Env, borrower: Address, loan_id: u32, amount: i128) -> Result<(), LoanError> {
@@ -2145,6 +2146,32 @@ impl LoanManager {
21452146
.unwrap_or(Vec::new(&env))
21462147
}
21472148

2149+
/// Returns loan IDs for a borrower filtered by status, without triggering accrual.
2150+
/// This allows indexers to query only loans in a specific state.
2151+
pub fn get_borrower_loans_by_status(
2152+
env: Env,
2153+
borrower: Address,
2154+
status: LoanStatus,
2155+
) -> Vec<u32> {
2156+
Self::bump_instance_ttl(&env);
2157+
let all_loans: Vec<u32> = env
2158+
.storage()
2159+
.instance()
2160+
.get(&DataKey::BorrowerLoans(borrower.clone()))
2161+
.unwrap_or(Vec::new(&env));
2162+
let mut matching_loans = Vec::new(&env);
2163+
for loan_id in all_loans.iter() {
2164+
let loan_key = DataKey::Loan(loan_id);
2165+
if let Some(loan) = env.storage().persistent().get::<DataKey, Loan>(&loan_key) {
2166+
Self::bump_persistent_ttl(&env, &loan_key);
2167+
if loan.status == status {
2168+
matching_loans.push_back(loan_id);
2169+
}
2170+
}
2171+
}
2172+
matching_loans
2173+
}
2174+
21482175
pub fn get_min_score(env: Env) -> u32 {
21492176
Self::bump_instance_ttl(&env);
21502177
env.storage()

0 commit comments

Comments
 (0)