feat: add set/get_max_loan_to_stake_ratio admin setter and tests (closes #102)#1
Open
feat: add set/get_max_loan_to_stake_ratio admin setter and tests (closes #102)#1
Conversation
- 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
…t_all_loans feature
…all-loans-paginated feat: implement get_all_loans paginated admin view (QuorumCredit#84)
…take-vouch feat: fix(issue-4): reject zero-stake vouches to prevent threshold inflation
feat: implement batch_slash admin function (QuorumCredit#56)
…an-extension Feat/issue 53 loan extension
…tch-vouch feat: implement batch_vouch function (issue QuorumCredit#55)
…acklist Feat/issue 54 blacklist
…ment-remove-voucher fix-implement-remove-voucher
…/issue-10-repay-balance-check fix(repay): pre-calculate total yield and assert sufficient contract …
…/issue-13-vouch-active-loan-check Fix/issue 13 vouch active loan check
…/issue-14-withdraw-vouch Fix/issue 14 withdraw vouch
…e-lockout security: enforce MIN_VOUCH_AGE to block same-tx vouch→loan attack
…mechanism Security/upgrade mechanism
…-events feat: emit events for all admin actions with admin address and timestamp
…protocol-fee-on-repayments feat: implement protocol fee on repayments (QuorumCredit#66)
…-max_vouchers_per_loan-cap Implement max vouchers per loan cap
…implement-loan_count-view-function Issue 68 implement loan count view function
…implement-default_count-view-function Issue 69 implement default count view function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes QuorumCredit#102
A borrower could previously request a loan far exceeding their total vouched stake. This PR adds a dedicated admin setter for
max_loan_to_stake_ratioso the collateral ratio can be updated independently without a fullset_config()call.Changes
src/lib.rsset_max_loan_to_stake_ratio(admin_signers, ratio)- new admin function to update the max loan-to-stake ratio (percentage). Rejects0with a clear error message.get_max_loan_to_stake_ratio()- new view function returning the current ratio.request_loanwas already present (amount <= total_stake * ratio / 100); this PR makes the ratio independently configurable at runtime.Tests Added (8 new tests)
test_max_loan_to_stake_ratio_default_is_150- Default ratio is 150%test_set_max_loan_to_stake_ratio_updates_config- Admin can update the ratiotest_set_max_loan_to_stake_ratio_zero_rejected- Zero ratio panics with clear messagetest_request_loan_at_exact_ratio_limit_succeeds- Loan exactly at the limit is allowedtest_request_loan_exceeds_ratio_rejected- Loan above the limit is rejectedtest_request_loan_below_ratio_limit_succeeds- Loan below the limit is allowedtest_increasing_ratio_allows_larger_loan- Raising the ratio unlocks larger loanstest_ratio_enforced_with_multiple_vouchers- Ratio uses sum of all voucher stakesSecurity Notes
0is explicitly rejected to prevent accidentally disabling the collateral check.