Audience: contributors — developers reading, modifying, or integrating with the QuickLendX smart contracts.
This document catalogs the most common read-only (query) entrypoints on the QuickLendXContract trait defined in quicklendx-contracts/src/lib.rs. Each entry shows the Rust signature, a concrete invocation example with realistic arguments, and the shape of the returned data.
Read queries never modify contract state. They are the primary way to inspect invoices, bids, investments, protocol configuration, and analytics from off-chain clients or other contracts.
- Configuration & Protocol State
- Invoice Queries
- Bid Queries
- Investment & Portfolio Queries
- Escrow Queries
- Settlement Queries
- KYC / Verification Queries
- Dispute Queries
- Analytics & Reporting
- Audit Queries
- Notification Queries
- Vesting Queries
- Pagination Conventions
- Error Handling
Returns the current ProtocolLimits struct — the tunable bounds that constrain invoice amounts, bid sizes, due-date windows, grace periods, and maximum invoices per business.
pub fn get_protocol_limits(env: Env) -> protocol_limits::ProtocolLimitsInvocation example (Soroban CLI / RPC):
{
"contract_id": "CCJZ5DGJ7Z3Q5KJ5KJ5KJ5KJ5KJ5KJ5KJ5KJ5KJ5",
"function": "get_protocol_limits",
"args": []
}Return value:
ProtocolLimits {
min_invoice_amount: 1_000_000, // 1 token (6 decimals)
min_bid_amount: 10, // minimum allowable bid
min_bid_bps: 100, // 1% minimum bid rate
max_due_date_days: 90,
grace_period_seconds: 2_592_000, // 30 days
max_invoices_per_business: 0, // 0 = unlimited
}Zero-config edge case: If the contract has never been initialized, this function still returns the hard-coded defaults defined in protocol_limits.rs.
pub fn is_initialized(env: Env) -> boolReturns true after initialize has been called. Before initialization, all mutating entrypoints return NotInitialized.
pub fn get_current_admin(env: Env) -> Option<Address>Returns Some(admin_address) if an admin has been set, None otherwise. Use this to verify admin configuration before calling admin-guarded entrypoints.
pub fn get_platform_fee(env: Env) -> types::PlatformFeeConfigReturn example:
PlatformFeeConfig {
fee_bps: 50, // 0.5%
treasury_address: Some(treasury_addr),
updated_at: 1700000000,
updated_by: admin_addr,
}pub fn is_allowed_currency(env: Env, currency: Address) -> boolQuery pattern: Before calling store_invoice with a token address, check whether it is whitelisted. When the whitelist is empty, every address passes (allow-all mode).
pub fn get_whitelisted_currencies_paged(env: Env, offset: u32, limit: u32) -> Vec<Address>Returns a page of whitelisted token addresses. For empty lists or out-of-range offsets, returns an empty vector.
pub fn is_paused(env: Env) -> bool
pub fn is_entrypoint_paused(env: Env, entrypoint: String) -> bool
pub fn is_maintenance_mode(env: Env) -> bool
pub fn get_maintenance_reason(env: Env) -> Option<String>Check these before dispatching mutating calls; paused entrypoints will reject with ContractPaused.
Returns the suggested default page size for fetching settlement (payment) records. This is a soft hint — not a hard enforcement — to help indexers and off-chain consumers standardize their pagination. The contract does not require callers to use this value.
pub fn get_settlement_batch_size_soft_cap(env: Env) -> u32Return value: 25 — recommended number of payment records per get_payment_records call.
Invocation example:
{
"function": "get_settlement_batch_size_soft_cap",
"args": []
}Usage pattern for indexers:
// Discover the hint at startup
let batch_size = contract.get_settlement_batch_size_soft_cap(env.clone());
// Paginate payment records using the hint
let mut offset = 0u32;
loop {
let page = contract.get_payment_records(env.clone(), invoice_id, offset, batch_size)?;
if page.is_empty() {
break;
}
// process page...
offset = offset.saturating_add(batch_size);
}Returns the hard upper bound for settlement batch queries. Requests to get_payment_records exceeding this value are silently clamped to this limit by the contract.
pub fn get_settlement_batch_size_soft_cap_max(env: Env) -> u32Return value: 50 — maximum number of payment records per query.
| Default | Max | |
|---|---|---|
| Settlement batch soft cap | 25 |
50 |
| Overdue scan batch limit | 25 |
100 |
General MAX_QUERY_LIMIT |
50 |
50 |
The primary read entrypoint for individual invoice data.
pub fn get_invoice(env: Env, invoice_id: BytesN<32>) -> Result<Invoice, QuickLendXError>Invocation example:
{
"function": "get_invoice",
"args": [
{ "type": "bytes", "value": "0xabc123def456abc123def456abc123def456abc123def456abc123def456abc1" }
]
}Return value (success):
Invoice {
id: 0xabc1…, // BytesN<32>
business: business_addr,
amount: 50_000_000_000, // 50,000 USDC (6 decimals)
currency: usdc_token_addr,
due_date: 1704067200, // Unix seconds
status: Verified,
created_at: 1701388800,
description: String("Q4 marketing campaign invoice"),
metadata_customer_name: Some("Acme Corp"),
metadata_customer_address: None,
metadata_tax_id: Some("12-3456789"),
metadata_notes: Some("Net-30 payment terms"),
metadata_line_items: Vec(…),
category: Services,
tags: Vec(["urgent", "quarterly"]),
funded_amount: 0,
funded_at: None,
investor: None,
settled_at: None,
average_rating: None,
total_ratings: 0,
ratings: Vec(…),
dispute_status: None,
dispute: Dispute { … },
total_paid: 0,
payment_history: Vec(…),
}Error cases:
| Condition | Error |
|---|---|
invoice_id not found |
Err(QuickLendXError::InvoiceNotFound) |
| Contract used before initialization | Err(QuickLendXError::NotInitialized) |
pub fn get_business_invoices_paged(
env: Env,
business: Address,
status_filter: Option<InvoiceStatus>,
offset: u32,
limit: u32,
) -> Vec<BytesN<32>>Lists invoice IDs for a given business, optionally filtered by status, with cursor-based pagination.
Example — first 10 active invoices:
let ids = contract.get_business_invoices_paged(
env.clone(),
business_addr,
Some(InvoiceStatus::Verified), // filter
0, // offset
10, // limit
);Edge cases:
offset >= total_count→ emptyVeclimit == 0→ emptyVeclimit > MAX_QUERY_LIMIT (100)→ capped toMAX_QUERY_LIMIT
pub fn get_available_invoices_paged(
env: Env,
min_amount: Option<i128>,
max_amount: Option<i128>,
category_filter: Option<InvoiceCategory>,
offset: u32,
limit: u32,
) -> Vec<BytesN<32>>The marketplace view — invoices available for bidding, with optional amount range and category filters.
pub fn search_invoices(env: Env, query: String) -> Result<Vec<SearchResult>, QuickLendXError>Full-text search across invoice descriptions, customer names, and tax IDs. Returns SearchResult entries ranked by relevance (ExactId > PartialMatch > Other).
pub fn get_total_invoice_count(env: Env) -> u32
pub fn get_invoice_count_by_status(env: Env, status: InvoiceStatus) -> u32Lightweight counters — useful for building dashboard summary cards without fetching full invoice data.
pub fn get_category_breakdown(env: Env) -> analytics::CategoryBreakdownReturns counts and total amounts per InvoiceCategory. Example caller: a marketplace overview page.
pub fn get_bid(env: Env, bid_id: BytesN<32>) -> Option<Bid>Returns None (not Err) when the bid does not exist — a missing bid is not an error.
Return value:
Some(Bid {
bid_id: 0xdef456…,
invoice_id: 0xabc123…,
investor: investor_addr,
bid_amount: 45_000_000_000, // 45,000 USDC
expected_return: 46_125_000_000, // 2.5% return
timestamp: 1701475200,
status: Placed,
expiration_timestamp: 1704067200,
})pub fn get_best_bid(env: Env, invoice_id: BytesN<32>) -> Option<Bid>Returns the highest-ranked bid for an invoice under the protocol's bid-ranking algorithm (lowest expected return wins). Returns None if no bids exist.
pub fn get_ranked_bids(env: Env, invoice_id: BytesN<32>) -> Vec<Bid>Full bid list sorted by rank. Useful for investor-facing bid comparison views.
pub fn get_investor_bids_paged(
env: Env,
investor: Address,
status_filter: Option<BidStatus>,
offset: u32,
limit: u32,
) -> Vec<Bid>History of all bids placed by a specific investor, with optional status filter (Placed, Accepted, Withdrawn, Expired, Cancelled).
pub fn get_investment(env: Env, investment_id: BytesN<32>) -> Result<Investment, QuickLendXError>Return value:
Investment {
investment_id: 0x…,
invoice_id: 0x…,
investor: investor_addr,
amount: 45_000_000_000,
funded_at: 1701561600,
status: Active,
insurance: Vec([InsuranceCoverage {
provider: insurance_provider_addr,
coverage_percentage: 80,
coverage_amount: 36_000_000_000,
premium_amount: 450_000_000,
active: true,
}]),
}pub fn get_investor_portfolio_summary(
env: Env,
investor: Address,
) -> Result<investment_queries::InvestorPortfolioSummary, QuickLendXError>Aggregated view — total active, completed, and defaulted investment amounts plus weighted-average return.
pub fn get_investor_investments_paged(
env: Env,
investor: Address,
status_filter: Option<InvestmentStatus>,
offset: u32,
limit: u32,
) -> Vec<BytesN<32>>Paginated list of investment IDs for a given investor. See Pagination Conventions for boundary behaviour.
pub fn get_active_investment_ids(env: Env) -> Vec<BytesN<32>>Returns the full list of currently active investment IDs. For large datasets prefer paged queries.
pub fn get_escrow_details(env: Env, invoice_id: BytesN<32>) -> Result<payments::Escrow, QuickLendXError>Return value:
Escrow {
escrow_id: 0x…,
invoice_id: 0x…,
investor: investor_addr,
business: business_addr,
amount: 45_000_000_000,
currency: usdc_token_addr,
created_at: 1701561600,
status: Held, // | Released | Refunded
}pub fn get_escrow_status(env: Env, invoice_id: BytesN<32>) -> Result<payments::EscrowStatus, QuickLendXError>Lighter-weight than get_escrow_details when only the status is needed. Returns Held, Released, or Refunded.
Returns a paginated slice of payment records for an invoice in chronological order. Indexers should page through these using get_settlement_batch_size_soft_cap as the recommended batch size.
pub fn get_payment_records(
env: Env,
invoice_id: BytesN<32>,
from: u32,
limit: u32,
) -> Result<Vec<settlement::SettlementPaymentRecord>, QuickLendXError>Parameters:
from: Starting index (0-based, inclusive)limit: Maximum records to return (capped atMAX_QUERY_LIMIT= 50)
Return value:
Vec<SettlementPaymentRecord {
payer: business_addr,
amount: 12_500_000_000,
timestamp: 1702051200,
nonce: String("tx_abc123"),
}>Recommended pagination:
let batch = contract.get_settlement_batch_size_soft_cap(env.clone()); // 25
let mut offset = 0u32;
loop {
let page = contract.get_payment_records(env.clone(), invoice_id.clone(), offset, batch)?;
if page.is_empty() { break; }
// process records...
offset = offset.saturating_add(batch);
}Returns aggregate payment progress for an invoice — total due, total paid, remaining due, progress percentage, payment count, and current status.
pub fn get_invoice_progress(
env: Env,
invoice_id: BytesN<32>,
) -> Result<settlement::Progress, QuickLendXError>Return value:
Progress {
total_due: 50_000_000_000,
total_paid: 25_000_000_000,
remaining_due: 25_000_000_000,
progress_percent: 50,
payment_count: 3,
status: Funded,
}Returns the total number of recorded payments for an invoice.
pub fn get_payment_count(env: Env, invoice_id: BytesN<32>) -> Result<u32, QuickLendXError>Returns whether an invoice has been settlement-finalized (i.e., its status has reached Paid and no further payments can be recorded).
pub fn is_invoice_finalized(env: Env, invoice_id: BytesN<32>) -> Result<bool, QuickLendXError>The contract exposes two read-only hints to help indexers discover the recommended and maximum page sizes for payment record queries:
| Entrypoint | Return value | Description |
|---|---|---|
get_settlement_batch_size_soft_cap |
25 |
Recommended default page size |
get_settlement_batch_size_soft_cap_max |
50 |
Hard upper bound enforced by the contract |
See Configuration & Protocol State for full documentation of these entrypoints.
pub fn get_business_verification_status(
env: Env,
business: Address,
) -> Option<verification::BusinessVerification>Returns None if the address has never submitted a KYC application. When Some, the struct contains submission timestamp, reviewer notes, and current status (Pending / Approved / Rejected).
pub fn get_investor_verification(
env: Env,
investor: Address,
) -> Option<InvestorVerification>Same pattern as business verification but scoped to investor data (tier, risk score, investment limit).
pub fn is_investor_verified(env: Env, investor: Address) -> boolQuick boolean check — returns true only when the investor has an approved verification with an active status.
pub fn get_verified_businesses(env: Env) -> Vec<Address>
pub fn get_pending_businesses(env: Env) -> Vec<Address>
pub fn get_rejected_businesses(env: Env) -> Vec<Address>Admin-facing lists for the verification dashboard. Each returns full address lists (not paginated; expected to be moderate size).
pub fn get_investors_by_tier(env: Env, tier: InvestorTier) -> Vec<Address>
pub fn get_investors_by_risk_level(env: Env, risk_level: InvestorRiskLevel) -> Vec<Address>Filtered investor lists. Used by the admin dashboard for cohort analysis.
pub fn calculate_investor_risk_score(
env: Env,
investor: Address,
kyc_data: String,
) -> Result<u32, QuickLendXError>Pure computation — does not write state. The risk score is derived from KYC data and on-chain behaviour metrics. Used by the front-end to preview what tier an applicant would land in before submitting KYC.
pub fn get_invoice_dispute_status(
env: Env,
invoice_id: BytesN<32>,
) -> Result<DisputeStatus, QuickLendXError>Returns the current dispute lifecycle phase: None, Disputed, UnderReview, or Resolved. Returns InvoiceNotFound if the invoice does not exist.
pub fn get_dispute_details(
env: Env,
invoice_id: BytesN<32>,
) -> Result<Option<Dispute>, QuickLendXError>Full dispute record including evidence, resolution outcome, and timestamps. Returns Ok(None) when no dispute has ever been opened.
pub fn get_dispute_timeline(
env: Env,
invoice_id: BytesN<32>,
offset: u32,
limit: u32,
) -> Result<dispute_timeline::DisputeTimeline, QuickLendXError>Paginated chronological events for a dispute (opened, evidence added, escalated, resolved). Timeline entries include the actor address and a block timestamp.
pub fn get_invoices_by_dispute_status(
env: Env,
dispute_status: DisputeStatus,
) -> Vec<BytesN<32>>Used by admin dashboards to surface invoices that need attention.
get_business_report and get_investor_report are read-only retrieval entrypoints for
previously generated report snapshots. The generation flow itself is documented in
QLX_REPORT_LIFECYCLE.md.
pub fn get_platform_metrics(env: Env) -> analytics::PlatformMetricsPlatform-wide aggregated data: total volume, total fees collected, active invoice count, active investor count.
pub fn get_performance_metrics(env: Env) -> analytics::PerformanceMetricsThroughput and health indicators: average time-to-fund, average time-to-payment, default rate, and dispute rate.
pub fn get_analytics_summary(env: Env)
-> (analytics::PlatformMetrics, analytics::PerformanceMetrics)One-shot combination of the two metric types above — saves an RPC round trip when building a dashboard overview.
pub fn get_financial_metrics(
env: Env,
period: analytics::TimePeriod,
) -> Result<analytics::FinancialMetrics, QuickLendXError>Time-windowed financial data (volume, fees, interest earned). Supported periods: Daily, Weekly, Monthly, Quarterly, Yearly, AllTime.
pub fn get_user_behavior_metrics(
env: Env,
user: Address,
) -> analytics::UserBehaviorMetricsPer-user engagement data: total actions, last active timestamp, action frequency.
pub fn get_address_summary(
env: Env,
addr: Address,
) -> Result<address_summary::AddressSummary, QuickLendXError>Unified view that aggregates invoices, bids, investments, and verification status for any address. The returned enum differentiates between business and investor roles. Returns AddressNotFound when the address has no on-chain activity.
pub fn get_invoice_audit_trail(
env: Env,
invoice_id: BytesN<32>,
) -> Vec<BytesN<32>>Returns audit entry IDs for the invoice, ordered by creation time. Each ID can be dereferenced with get_audit_entry.
pub fn get_audit_entry(
env: Env,
audit_id: BytesN<32>,
) -> Option<audit::AuditLogEntry>Single audit event: operation type, actor, affected entity, timestamp, and a cryptographic link (hash-chain) to the previous entry for integrity verification.
pub fn query_audit_logs(
env: Env,
filter: audit::AuditQueryFilter,
limit: u32,
) -> Vec<audit::AuditLogEntry>Advanced search across audit logs. The AuditQueryFilter supports filtering by operation type, actor address, and time range.
pub fn verify_audit_chain(env: Env, invoice_id: BytesN<32>) -> bool
pub fn validate_invoice_audit_integrity(
env: Env,
invoice_id: BytesN<32>,
) -> Result<bool, QuickLendXError>Integrity checks that verify the hash chain has not been tampered with. Returns false / Err if a gap or hash mismatch is detected.
pub fn get_user_notifications(
env: Env,
user: Address,
) -> Vec<BytesN<32>>Returns notification IDs for a user, newest first. Dereference each ID with get_notification.
pub fn get_notification(
env: Env,
notification_id: BytesN<32>,
) -> Option<notifications::Notification>Single notification: type (e.g. BidAccepted, InvoicePaid), title, body, read status, and timestamp.
pub fn get_user_notification_stats(
env: Env,
user: Address,
) -> notifications::NotificationStatsUnread count and total count — useful for badge display in a UI header.
pub fn get_vesting_schedule(env: Env, id: u64) -> Option<vesting::VestingSchedule>Returns the schedule configuration: total amount, cliff duration, vesting duration, start time, and beneficiary.
pub fn get_vesting_vested(env: Env, id: u64) -> Option<i128>Amount currently vested (not yet released). Returns None if the schedule ID does not exist.
pub fn get_vesting_releasable(env: Env, id: u64) -> Option<i128>Amount that the beneficiary can release right now (vested minus already released).
pub fn get_vesting_summary(env: Env, user: Address) -> vesting::VestingSummaryAggregate across all schedules for a user: total vested, total released, total remaining, next release time.
All paginated endpoints (*_paged) follow the same conventions. For a detailed specification of ordering guarantees, snapshot consistency, cursor stability, and multi-page concurrency behavior, see Query Semantics & Paged Read Guarantees.
| Rule | Behaviour |
|---|---|
| Max page size | MAX_QUERY_LIMIT = 100 — larger values are silently capped |
| Zero limit | Returns empty Vec |
| Offset at or past end | Returns empty Vec |
| Offset == 0 | First page |
| Overflow safety | All internal arithmetic uses saturating operations |
| Filtering order | Status filters are applied before pagination slicing |
Example — safe iteration over all invoices for a business:
let mut offset = 0u32;
let limit = 100u32;
loop {
let page = contract.get_business_invoices_paged(
env.clone(),
business_addr,
None,
offset,
limit,
);
if page.is_empty() {
break;
}
// process page
offset = offset.saturating_add(limit);
}Read queries return errors in two patterns:
| Pattern | Convention | Examples |
|---|---|---|
Result<T, QuickLendXError> |
Missing resource returns Err |
get_invoice, get_investment, get_escrow_details |
Option<T> |
Missing resource returns None |
get_bid, get_vesting_schedule, get_notification |
The Option pattern is used for entity types that are expected to be missing in normal operation (e.g. "no bid yet for this invoice"). The Result pattern is used when a missing resource indicates a programmer error or invalid state.
All error variants are documented in docs/contracts/errors.md.
- Full contract API —
lib.rs: Source code with inline rustdoc for every entrypoint. docs/contracts/queries.md: Deep dive into the investment-query pagination implementation, overflow safety, and boundary testing.docs/CURRENCY_WHITELIST.md: How the currency allow-list affects invoice and bid entrypoints.docs/INVESTOR_TIER.md: Risk score and tier computation used by the verification queries.docs/VESTING.md: Full vesting model with worked examples.