Soroban smart contracts for the InvoFi decentralised invoice financing protocol, built with Rust + Soroban SDK 22 on Stellar.

InvoiceRegistryContract governs the full lifecycle of invoice financing on Stellar — from registration through partial repayment, dispute resolution, and overdue handling. All state lives on-chain; no intermediary holds funds.
register_invoice() → create_offer() → accept_offer()
↓ ↓
[Pending] funds to business
↓ ↓
reject_offer() [Financed]
stays Pending ↓
repay_invoice() (partial or full)
↓
[Repaid] ← balance cleared
[Overdue] ← mark_overdue()
↓
reclaim_invoice() (after 7-day grace)
offer → [Defaulted]
raise_dispute() → [Disputed]
resolve_dispute() → [Financed] or [Cancelled]
| Function |
Auth |
Description |
initialize(admin, token) |
Anyone (once) |
One-time setup — sets admin and SEP-41 token |
register_invoice(id, originator, amount, currency, due_date) |
Originator |
Register invoice; rejects if amount < MIN_INVOICE_AMOUNT or due_date <= now |
get_invoice(id) |
Anyone |
Read invoice state |
cancel_invoice(id, originator) |
Originator |
Cancel a Pending invoice |
update_invoice_status(id, status) |
Originator |
Manual status override |
create_offer(offer_id, invoice_id, lender, amount, currency, rate, duration) |
Lender |
Submit offer; validates amount, rate, duration <= MAX_OFFER_DURATION_SECS |
get_offer(id) |
Anyone |
Read offer state |
accept_offer(offer_id, originator) |
Originator |
Accept offer — transfers principal to business; invoice → Financed |
reject_offer(offer_id, originator) |
Originator |
Reject a pending offer |
repay_invoice(invoice_id, offer_id, repayer, amount) |
Originator |
Partial or full repayment; offer → Repaid when balance cleared |
mark_overdue(invoice_id) |
Anyone |
Mark a past-due Financed invoice Overdue |
reclaim_invoice(invoice_id, offer_id, lender) |
Lender |
After 7-day grace period, marks offer Defaulted |
calculate_total_due(offer_id) |
Anyone |
Remaining principal + accrued yield |
| Function |
Auth |
Description |
get_invoices_by_status(status) |
Anyone |
All invoices with a given status |
get_invoices_by_currency(currency) |
Anyone |
Invoices denominated in a given asset symbol |
get_invoices_due_before(timestamp) |
Anyone |
Open invoices with due_date < timestamp |
get_invoices_count() |
Anyone |
Total invoices ever registered |
get_invoices_paginated(offset, limit) |
Anyone |
Page through all invoices |
batch_get_invoices(ids) |
Anyone |
Fetch multiple invoices by ID; skips missing IDs |
get_offers_by_invoice(invoice_id) |
Anyone |
All offers for an invoice |
get_offers_by_lender(lender) |
Anyone |
All offers by a lender |
get_offers_by_status(status) |
Anyone |
All offers matching a status |
get_pending_offers_by_invoice(invoice_id) |
Anyone |
Only Pending offers on an invoice |
get_offers_count() |
Anyone |
Total offers ever created |
get_offers_paginated(offset, limit) |
Anyone |
Page through all offers |
| Function |
Auth |
Description |
get_lender_stats(lender) |
Anyone |
LenderStats — total_offered, total_accepted, offers_pending, offers_repaid |
get_lender_active_total(lender) |
Anyone |
Sum of amounts across all Accepted offers for a lender |
| Function |
Auth |
Description |
raise_dispute(invoice_id, originator) |
Originator |
Mark a Financed invoice Disputed |
resolve_dispute(admin, invoice_id, target_status) |
Admin |
Resolve Disputed invoice to Financed or Cancelled |
| Function |
Auth |
Description |
get_stats() |
Anyone |
ProtocolStats — total_invoices, total_offers, total_financed, total_repaid, total_fee_revenue |
| Function |
Auth |
Description |
set_rate(admin, tier, rate_bps) |
Admin |
Set yield rate for risk tier A/B/C |
get_rate(tier) |
Anyone |
Read yield rate for tier |
set_fee(admin, fee_bps) |
Admin |
Set protocol fee (max 500 bps = 5%) |
get_fee() |
Anyone |
Read current fee |
transfer_admin(admin, new_admin) |
Admin |
Rotate admin address |
get_admin() |
Anyone |
Read admin address |
get_token() |
Anyone |
Read SEP-41 token address |
pause(admin) |
Admin |
Halt all state-mutating operations |
unpause(admin) |
Admin |
Resume normal operation |
contract_is_paused() |
Anyone |
Query pause state |
blacklist_address(admin, target) |
Admin |
Block address from registering invoices/offers |
unblacklist_address(admin, target) |
Admin |
Remove block |
is_blacklisted(address) |
Anyone |
Query blacklist |
get_blacklist() |
Anyone |
Return full blacklist |
| Function |
Auth |
Description |
version() |
Anyone |
Contract semver string |
get_min_invoice_amount() |
Anyone |
MIN_INVOICE_AMOUNT constant |
get_offer_duration_limits() |
Anyone |
(MIN_OFFER_DURATION_SECS, MAX_OFFER_DURATION_SECS) |
Every state-mutating function publishes a Soroban contract event (v0.3.0+).
Topics are (event_name, subject_id) — indexers can filter by invoice or
offer id without decoding payloads.
| Event |
Emitted by |
Data payload |
inv_reg |
register_invoice |
(originator, amount, due_date) |
off_new |
create_offer |
(invoice_id, lender, amount, interest_rate) |
off_acc |
accept_offer |
(invoice_id, lender, amount) |
off_rej |
reject_offer |
invoice_id |
off_wdr |
withdraw_offer |
lender |
off_def |
reclaim_invoice |
(invoice_id, lender) |
inv_rep |
repay_invoice |
(offer_id, amount, fully_repaid) |
inv_ovd |
mark_overdue |
due_date |
inv_cxl |
cancel_invoice |
originator |
inv_dsp |
raise_dispute |
originator |
inv_rsl |
resolve_dispute |
new_status |
| Constant |
Value |
Description |
GRACE_PERIOD_SECS |
604,800 |
7-day grace period before lender can reclaim |
MIN_OFFER_DURATION_SECS |
86,400 |
Minimum offer duration (1 day) |
MAX_OFFER_DURATION_SECS |
31,536,000 |
Maximum offer duration (1 year) |
MIN_INVOICE_AMOUNT |
10,000,000 |
Minimum invoice amount in stroops (10 XLM / 10 USDC) |
# Build
cargo build --target wasm32v1-none --release
# Run tests (30+ tests)
cargo test
# Check WASM size stays under 256 KB
bash scripts/check-size.sh
# Deploy to Testnet (requires stellar-cli)
bash scripts/deploy.sh
Or trigger the Deploy Contract GitHub Actions workflow for a one-click Testnet deploy.
See CHANGELOG.md for version history.
See CONTRIBUTING.md for build, test, and PR guidelines.
MIT © 2026 InvoFi Contributors