Skip to content

comparing features#1

Open
euniceamoni wants to merge 109 commits into
feat/voucher-cap-per-loanfrom
main
Open

comparing features#1
euniceamoni wants to merge 109 commits into
feat/voucher-cap-per-loanfrom
main

Conversation

@euniceamoni

@euniceamoni euniceamoni commented Mar 26, 2026

Copy link
Copy Markdown
Owner

Nexha-dev and others added 30 commits March 24, 2026 15:59
- Add DataKey::BorrowerList to track all borrowers who have taken loans
- Append borrower to BorrowerList on each request_loan call
- Add get_all_loans(page, page_size) admin-only paginated view
- Add tests: pagination correctness and empty state
- Fix pre-existing bug: duplicate initialize() calls in test setup
- Add grace_period: u64 to Config struct (default 3 days)
- Add DEFAULT_GRACE_PERIOD constant (3 * 24 * 60 * 60 seconds)
- auto_slash now enforces timestamp > deadline + grace_period using
  saturating_add to prevent u64 overflow
- grace_period = 0 is valid and restores original deadline-only behaviour
- set_config accepts any grace_period value (0 is a valid no-grace config)
- Add 8 unit tests: blocked during grace period, allowed after, boundary
  at exact threshold, one second past threshold, zero grace period,
  blocked on repaid loan, default value check, set_config update

Closes QuorumCredit#64
- Add DEFAULT_GRACE_PERIOD constant (3 days in seconds)
- Add grace_period: u64 to Config struct with doc comment
- Config::default() initialises grace_period to DEFAULT_GRACE_PERIOD
- auto_slash enforces timestamp > deadline.saturating_add(grace_period)
  using saturating_add to prevent u64 overflow
- grace_period = 0 is valid; restores immediate post-deadline slashing
- set_config comment clarifies 0 is an accepted value
- 9 unit tests: blocked during grace period, allowed after, exact
  boundary rejection, one-second-past-threshold, zero grace period,
  blocked on repaid loan, blocked on already-defaulted loan, default
  value assertion, set_config round-trip

Closes QuorumCredit#65
)

- Add extend_loan(admin_signers, borrower, new_deadline) admin function
- Add consent_extension(voucher, borrower) for voucher consent tracking
- Add get_extension_consents(borrower) view function
- Add ExtensionConsents storage key and LoanNotActive/DeadlineNotExtended errors
- Add 6 tests covering happy path, auth rejection, and edge cases
- Extract do_vouch private helper; vouch delegates to it
- Add batch_vouch(voucher, borrowers, stakes) — validates lengths match,
  rejects empty batches, aborts atomically on any per-entry error
- Add 4 tests: happy path, length mismatch, empty batch, duplicate abort
…balance before payout loop

Fixes QuorumCredit#10

- Compute total_payout (stake + proportional yield) for all vouchers
  before entering the transfer loop
- Return ContractError::InsufficientFunds early if contract balance
  cannot cover the full payout, preventing a mid-loop panic
- Removed duplicate/malformed 'if fully_repaid' block that referenced
  an undefined variable
Fixes QuorumCredit#13

- Added ContractError::BorrowerHasActiveLoan = 14
- Check has_active_loan before accepting stake transfer in vouch
- Prevents voucher stake being locked with no effect on existing loan
Fixes QuorumCredit#14

- Replace raw .is_none() storage check with has_active_loan() so repaid
  or defaulted loan records no longer permanently block vouch withdrawal
- Return ContractError::PoolBorrowerActiveLoan instead of panicking
- Return ContractError::UnauthorizedCaller when voucher not found
- Add require_not_paused guard
- Emit vouch/withdrawn event on success
- Change return type to Result<(), ContractError>
… balance

Fixes QuorumCredit#15

- Add total_yield field to LoanRecord, locked in at disbursement
- Borrower now repays loan.amount + loan.total_yield total
- outstanding = (amount + total_yield) - amount_repaid
- Vouchers receive stake + proportional yield funded entirely by borrower
- Removes pre-funded balance dependency and InsufficientFunds balance check
- Apply same total_yield calculation in create_loan_pool disbursement
Add a 60-second minimum age check on all vouches before a loan can be
requested. Vouches used in the same transaction (or block) as the loan
request share an identical ledger timestamp, so the check

  now < vouch_timestamp + MIN_VOUCH_AGE  →  VouchTooRecent

reliably blocks the vouch → request_loan → repay flash-loan pattern.

Changes:
- Add MIN_VOUCH_AGE = 60 constant
- Add ContractError::VouchTooRecent (variant 14)
- Enforce age check in request_loan after min_vouchers guard
- Add advance_past_vouch_age() test helper
- Update all existing tests to advance the clock between vouch and loan
- Add test_request_loan_rejects_vouch_too_recent
- Add test_request_loan_succeeds_after_vouch_age_elapsed
The upgrade() function already existed but had no tests and no
documented upgrade process. This commit completes the feature.

Contract:
- upgrade(admin_signers, new_wasm_hash) calls
  env.deployer().update_current_contract_wasm() after verifying
  admin_threshold signatures — same multisig quorum as all other
  admin operations
- Emits an 'upgrade' event with the new WASM hash for auditability
- A single compromised admin key cannot unilaterally upgrade

Tests added:
- test_upgrade_rejected_without_admin_approval
- test_upgrade_multisig_rejects_single_signer

Docs:
- README: full upgrade process documented (pause → install → upgrade → unpause)
- Explains why admin_threshold is required and what the WASM hash represents
Admin functions previously had no on-chain audit trail. This adds
events to every state-mutating admin function:

- pause        → ("admin", "pause",    admin, timestamp)
- unpause      → ("admin", "unpause",  admin, timestamp)
- set_min_stake     → ("admin", "minstake", admin, amount, timestamp)
- set_max_loan_amount → ("admin", "maxloan",  admin, amount, timestamp)
- set_min_vouchers  → ("admin", "minvchrs", admin, count, timestamp)
- set_config        → ("admin", "config",   admin, timestamp)
- set_protocol_fee  → ("admin", "fee",      admin, fee_bps, timestamp)
- set_reputation_nft → ("admin", "repnft",  admin, nft_contract, timestamp)
- slash_treasury    → ("admin", "treasury", admin, recipient, amount, timestamp)

upgrade and slash already emitted events — unchanged.

Tests added for every new event.
Admin actions (slash, config changes) previously took effect instantly,
giving users no time to react. This adds a two-phase timelock:

  propose_action(admin_signers, action) -> proposal_id
  execute_action(admin_signers, proposal_id)  [after TIMELOCK_DELAY]
  cancel_action(admin_signers, proposal_id)

Constants:
  TIMELOCK_DELAY  = 24 hours
  TIMELOCK_EXPIRY = 72 hours

Supported actions (TimelockAction enum):
  Slash(borrower)   - timelocked slash path
  SetConfig(config) - timelocked config update (covers admin transfer)

Events:
  (tl, proposed)  -> (id, proposer, eta)
  (tl, executed)  -> (id, admin, timestamp)
  (tl, cancelled) -> (id, admin)
 QuorumCredit#102)

- Add set_max_loan_to_stake_ratio() admin function to update the ratio
  without requiring a full set_config() call
- Add get_max_loan_to_stake_ratio() view function
- Add 7 dedicated tests covering:
  - Default ratio value (150%)
  - Admin setter updates config
  - Zero ratio rejected
  - Loan at exact ratio limit succeeds
  - Loan exceeding ratio is rejected
  - Loan below ratio limit succeeds
  - Increasing ratio allows larger loans
  - Ratio enforced correctly with multiple vouchers
…edit#103)

Security: a single compromised admin key can no longer unilaterally
control slash, config, or treasury operations.

Changes:
- Add add_admin(): quorum-gated admin set expansion
- Add remove_admin(): quorum-gated removal; blocked when removal would
  make the threshold unsatisfiable
- Add rotate_admin(): atomic key rotation preserving count and threshold;
  the primary mitigation for a compromised key
- Add set_admin_threshold(): quorum-gated threshold update
- All four functions emit on-chain events for auditability
- Add 22 dedicated multisig security tests covering:
  - Threshold enforcement (zero signers, below threshold, exact, above)
  - Non-admin signer rejection
  - add_admin: success, duplicate rejection, quorum requirement
  - remove_admin: success, unsatisfiable threshold guard, unknown address,
    quorum requirement
  - rotate_admin: success, same-address rejection, existing-admin-as-new
    rejection, unknown old admin, quorum requirement
  - set_admin_threshold: success, zero rejection, exceeds-count rejection,
    quorum requirement
  - End-to-end: rotated-in key can operate; rotated-out key is rejected
  - validate_admin_config: empty admins, duplicate admins, threshold > count
- Document key management recommendations in CONTRIBUTING.md:
  recommended M-of-N configurations, hardware wallet guidance, rotation
  procedures, and a function reference table
…all-loans-paginated

feat: implement get_all_loans paginated admin view (QuorumCredit#84)
chukwudiikeh and others added 24 commits March 26, 2026 06:43
…lash-function

feat: add slash assertions
Closes QuorumCredit#23 Fix: initialize does not emit an event
 Issue 24 Fix: initialize does not validate admin or token are non-zero addresses
Fix YIELD_BPS and SLASH_BPS hardcoded constants
Fixed Integer Overflow Risk in Total Stake Summation
…nt-timestamp-field

feat: add repayment_timestamp
fix: validate token address implements SEP-41 on initialize
- QuorumCredit#74: governance vote_slash with stake-weighted quorum auto-execution
- QuorumCredit#75: loan_purpose field on LoanRecord, accepted in request_loan
- QuorumCredit#76: multi-asset loan support (USDC/any token), allowed_tokens in Config
- QuorumCredit#77: referral bonus for vouchers on successful borrower repayment
euniceamoni pushed a commit that referenced this pull request May 29, 2026
euniceamoni pushed a commit that referenced this pull request May 29, 2026
test: add multi-token vouch test (XLM + USDC)
chukwudiikeh pushed a commit that referenced this pull request May 30, 2026
Add successor_admin to Config for fallback admin takeover
euniceamoni pushed a commit that referenced this pull request Jun 29, 2026
Closes #1

## Summary
Implements a fully functional real-time loan status dashboard for borrowers,
displaying active loans, repayment progress, yield earned, and reputation tier
with live updates streamed over socket.io WebSocket.

## New Files

### dashboard/src/stroops.ts
- Exports `stroopsToXlm(stroops)` — converts stroop amounts (number | bigint)
  to XLM string with exactly 7 decimal places (e.g. 10_000_000 → "1.0000000")
- Exports `xlmToStroops(xlm)` — converts XLM float to stroops integer (rounded)
- Exports `STROOPS_PER_XLM = 10_000_000` constant
- Handles 0, negative, large integers, and bigint inputs safely

### dashboard/src/loanSlice.ts
- Redux Toolkit slice for loan state management
- Types: `LoanRecord`, `VouchRecord`, `ReputationInfo`, `LoanStatus`
- Actions: `upsertLoan` (add or update by id), `setLoans` (replace all),
  `setReputation`, `setConnected`, with `lastUpdated` timestamp tracking

### dashboard/src/store.ts
- Configures Redux store with `loanReducer`
- Exports `RootState` and `AppDispatch` types

### dashboard/src/useLoanSocket.ts
- React hook using socket.io-client to connect to the loan-status server
- Dispatches Redux actions on socket events:
  - `connect` → `setConnected(true)`
  - `disconnect` → `setConnected(false)`
  - `loan:update` → `upsertLoan`
  - `loan:list` → `setLoans`
  - `reputation` → `setReputation`
- Emits `subscribe` with borrower address on mount
- Supports optional `apiKey` in socket auth header
- Cleans up and dispatches `setConnected(false)` on unmount

### dashboard/src/LoanCard.tsx
- Displays a single `LoanRecord` with:
  - Borrower address, loan purpose
  - Principal and yield amounts converted from stroops to XLM (7 d.p.)
  - Repayment deadline
  - Status badge (Active / Repaid / Defaulted) with colour coding
  - Animated repayment progress bar capped at 100%
- Fully accessible: `role="article"`, `role="progressbar"` with
  `aria-valuenow/min/max`, `aria-label` on status badge
- Mobile-responsive flex layout

### dashboard/src/LoanStatusDashboard.tsx
- Top-level component wrapping its own Redux `Provider`
- Props: `borrower`, `wsUrl`, optional `apiKey`
- Sections: live indicator, reputation tier/score, active loans, closed loans
- Shows "Updated HH:MM:SS" timestamp from `lastUpdated`
- Delegates WebSocket lifecycle to `useLoanSocket`

### dashboard/src/__tests__/loanDashboard.test.tsx
- 33 tests across 4 suites (all passing):
  - `stroopsToXlm` — 8 cases: zero, one stroop, large value, bigint, negative,
    7 d.p. precision, constant value, XLM-to-stroops rounding
  - `loanSlice` — 6 cases: setConnected, upsertLoan (add + update), setLoans,
    setReputation, lastUpdated tracking
  - `useLoanSocket` — 5 cases: connect/disconnect dispatch, loan:update,
    loan:list, subscribe emit; uses module-level vi.mock for socket.io-client
  - `LoanCard` — 11 cases: borrower, purpose, principal XLM, yield XLM,
    Active/Repaid/Defaulted badges, progress bar value, 0% edge, 100% cap,
    accessible article label

## Dependencies Added
- socket.io-client — WebSocket client for real-time loan updates
- @reduxjs/toolkit + react-redux — state management for loan data
- tailwindcss — CSS utility framework (available for future Tailwind usage)
- @types/socket.io-client — TypeScript types

## Test Results
63 tests passing (33 new + 12 AnalyticsDashboard + 18 analytics), 0 failing
euniceamoni pushed a commit that referenced this pull request Jun 29, 2026
feat: store custom attributes, stream yield, vouch groups, periodic payments
euniceamoni pushed a commit that referenced this pull request Jun 29, 2026
…ression-tests

fix(contract): resolve 4 bugs and add incentive + regression test suites
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.