Decentralized, risk-priced credit on Stellar / Soroban — without overcollateralization. Credit lines whose limit and interest rate evolve continuously from on-chain behavioral signals, financial attestations, and a formally specified risk-pricing function. Default events are settled through a separate auction contract using a one-shot, replay-protected cross-contract handoff.
This is the Creditra-Contracts workspace: Soroban WebAssembly contracts for
the credit line, the risk contract, and the auction handoff, release WASM under
a 50 KB hard CI budget. Line coverage is not claimed as a number here: CI
measures it on every run and fails the build below the enforced floor — see
docs/COVERAGE.md for the current floor and measured value.
| Doc | What it answers |
|---|---|
WHITEPAPER.md |
Why and how — protocol-level model, math, comparison vs Aave/Compound/Maker |
docs/INDEX.md |
Audience-routed entry point (reviewer / auditor / integrator / operator / contributor) |
docs/PROTOCOL_SPEC.md |
Per-module contract surface: every entrypoint, every storage key, every error |
docs/ARCHITECTURE.md |
System & sequence diagrams (mermaid); call topology |
docs/RISK_PRICING.md |
The risk-pricing algorithm in depth, with worked numerical examples |
docs/SECURITY.md |
Threat model, auditor checklist, bug bounty scope |
docs/EXECUTION_QUALITY.md |
Test catalog, CI matrix, deployment checklists, PR cadence |
docs/GLOSSARY.md |
Project terminology with source citations |
Aave / Compound / Maker require 150 %+ overcollateralization, which gates the median wallet out of on-chain credit. Creditra computes a credit limit and an interest rate from a deterministic on-chain function of the borrower's behavioral history and risk score:
— where contracts/credit/src/risk.rs:77). The
contract supports an optional collateral floor (default 150 %) that an
operator can dial between fully unsecured and Aave-style — but the eligibility
predicate is behavior, not deposit.
See WHITEPAPER.md for the full design.
flowchart LR
Borrower((Borrower)) -->|"draw / repay /<br/>self_suspend / close"| Credit
Admin((Admin / Multisig)) -->|"init, set_*, update_risk_parameters,<br/>default, settle, upgrade"| Credit
Scorer((Off-chain Scorer)) -.->|"risk_score"| Admin
Credit -->|"transfer / transfer_from"| Token[Liquidity Token SAC]
Credit -->|"reserve I/O"| Reserve[Liquidity Source]
Credit -->|"settle_default_liquidation<br/>(cross-contract)"| Auction
Auction -->|"highest_bid (i128)"| Credit
Credit -->|events| Indexer((Event Indexer))
Auction -->|events| Indexer
| Crate | Path | Role |
|---|---|---|
creditra-credit |
contracts/credit/ |
Credit-line core: open / draw / repay / risk update / default / settle / upgrade |
creditra-risk |
contracts/risk/ |
Standalone risk admin cooldown contract: time-based circuit breaker for admin risk-mutation actions |
gateway-auction |
gateway-contract/contracts/auction_contract/ |
Minimal English & Dutch auction; one-shot settlement handoff back to credit |
creditra-accrual |
contracts/accrual/ |
Test/indexer support crate: error stability for accrual (not deployed). |
creditra-borrow |
contracts/borrow/ |
Test/indexer support crate: error stability for borrow (not deployed). |
creditra-freeze |
contracts/freeze/ |
Test/indexer support crate: auth boundary coverage for freeze (not deployed). |
creditra-lifecycle |
contracts/lifecycle/ |
Test/indexer support crate: read-only bitmap for lifecycle transitions (not deployed). |
creditra-query |
contracts/query/ |
Test/indexer support crate: capabilities view and events (not deployed). |
Full module catalog and entrypoint signatures: docs/PROTOCOL_SPEC.md.
Sequence diagrams for draw, repay, default → auction → settle:
docs/ARCHITECTURE.md.
- Rust — the exact compiler is pinned in
rust-toolchain.toml; anyrustup-shippedcargoinstalls and uses it automatically. Floating channels (stable/beta/nightly) are rejected byscripts/check-toolchain.shso builds stay reproducible across toolchain versions. wasm32-unknown-unknowntarget (declared inrust-toolchain.toml; installed automatically with the toolchain):rustup target add wasm32-unknown-unknown
- Stellar Soroban CLI for deploy/invoke.
# Workspace build (no WASM)
cargo build
# Release WASM, size-optimized
cargo build --release --target wasm32-unknown-unknown -p creditra-credit
# Output: target/wasm32-unknown-unknown/release/creditra_credit.wasmThe release profile (Cargo.toml for workspace members and
contracts/creditra-credit/Cargo.toml for the standalone credit crate) is
tuned for contract size: opt-level = "z", lto = true, strip = "symbols",
codegen-units = 1, panic = "abort", and overflow-checks = true, keeping
arithmetic checked even in release — the entire i128 accounting layer reverts
on overflow instead of wrapping, so contracts trade gas for safety.
scripts/check-overflow-checks.sh fails the build if either release profile
loses that setting, and both scripts/check_workspace.sh and
scripts/build_wasm.sh run it before compiling.
Builds are reproducible across machines and over time because the whole workspace compiles with one pinned toolchain against pinned dependencies:
rust-toolchain.tomlpinschannelto an exactX.Y.Zcompiler version;scripts/check-toolchain.shfails the build on any floating channel and--verify-activefails when the activerustcdiffers from the pin.- All build/test entry points compile
--lockedagainst committedCargo.lockfiles, so dependency resolution cannot drift. - CI reads the same
rust-toolchain.toml(no floating toolchain refs), so local and CI artifacts come from identical inputs.
cargo test --workspaceMeasured and enforced in CI by the coverage job in
.github/workflows/ci.yml, over
contracts/creditra-credit — the crate that job actually builds and tests.
The job fails below MIN_LINE_COVERAGE, publishes the HTML report as the
coverage-report artifact, and writes the measured numbers to its job summary.
cargo install cargo-llvm-cov --version 0.9.1 --locked
cd contracts/creditra-credit
# Reproduce the CI gate
cargo llvm-cov --all-targets --html --fail-under-lines 92The floor, the measured value, and the reason the root Soroban workspace is not
yet included are documented in docs/COVERAGE.md.
A full working deployment requires initializing the credit contract and wiring it to the auction contract.
# 1. Deploy the contract
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/creditra_credit.wasm \
--source <identity> --network testnet
# 2. Initialize
soroban contract invoke --id <addr> --source <identity> --network testnet -- init --admin <admin-addr>
# 3. Set liquidity token (required for drawing)
soroban contract invoke --id <addr> --source <admin-identity> --network testnet -- set_liquidity_token --token_address <token-addr>
# 4. Set liquidity source (WARNING: Unsafe default uses contract's own address)
soroban contract invoke --id <addr> --source <admin-identity> --network testnet -- set_liquidity_source --reserve_address <reserve-addr>
# 5. Set minimum collateral ratio (optional)
soroban contract invoke --id <addr> --source <admin-identity> --network testnet -- set_min_collateral_ratio_bps --ratio_bps 15000
# 6. Wire auction contract
soroban contract invoke --id <addr> --source <admin-identity> --network testnet -- set_auction_contract --auction_contract <auction-addr>
soroban contract invoke --id <auction-addr> --source <auction-admin-identity> --network testnet -- set_factory_contract --factory <addr>For a comprehensive guide on the deployment sequence and invariants, see docs/deploy.md.
Full testnet + mainnet checklists are in
docs/EXECUTION_QUALITY.md §6.
Creditra-Contracts/
├── WHITEPAPER.md # Protocol-level design (this is the centerpiece)
├── README.md # You are here
├── Cargo.toml # Workspace + release profile
├── contracts/credit/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # #[contract] Credit + all entrypoints
│ ├── types.rs # ContractError, CreditStatus, configs
│ ├── storage.rs # DataKey, TTL constants, helpers
│ ├── auth.rs # require_admin / require_admin_auth
│ ├── config.rs # init, set_liquidity_*
│ ├── borrow.rs # draw_status_error helper
│ ├── collateral.rs # deposit/withdraw + MinCollateralRatioBps
│ ├── freeze.rs # global draws-frozen toggle
│ ├── lifecycle.rs # state transitions + settle_default_liquidation
│ ├── risk.rs # compute_rate_from_score, update_risk_parameters
│ ├── accrual.rs # apply_accrual + grace/penalty branches
│ ├── math_utils.rs # mul_div, prorate_interest, Rounding
│ ├── query.rs # read-only helpers, is_delinquent
│ └── events.rs # #[contracttype] payload structs
│ └── tests/ # Integration test files
├── contracts/accrual/ # Test/indexer support wrapper crates
├── contracts/borrow/ # Re-exports the credit contract for testing/indexing
├── contracts/collateral/
├── contracts/freeze/
├── contracts/lifecycle/
├── contracts/query/
├── contracts/risk/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # #[contract] RiskContract + entrypoints
│ └── admin.rs # cooldown storage helpers + guard
├── gateway-contract/contracts/auction_contract/
│ ├── tests/
│ │ ├── transition_matrix.rs # AuctionStatus transition matrix (Issue #614)
│ │ └── auth_settle.rs # settle_default_liquidation auth coverage
│ └── src/
│ ├── lib.rs # Auction contract (English + Dutch modes)
│ ├── types.rs # AuctionMode, AuctionStatus, AuctionState
│ ├── storage.rs # DataKey + persistent AuctionKey, TTLs
│ ├── events.rs # BidRefundedEvent, AuctionClosedEvent, ...
│ ├── errors.rs # AuctionError
│ └── test.rs # Tests
├── docs/ # Long-form references (state machine, errors,
│ # storage layout, threat model, accrual,
│ # rate formula, indexer integration, …)
└── scripts/ # Operator helpers (build, check, error introspection)
Per-entrypoint signatures, validation order, storage keys, and error returns:
docs/PROTOCOL_SPEC.md.
Credit (#[contract], #[contractimpl] in contracts/credit/src/lib.rs):
- Init & admin rotation:
init,propose_admin,accept_admin,get_contract_version. - Credit-line CRUD:
open_credit_line,draw_credit,repay_credit,close_credit_line,suspend_credit_line,self_suspend_credit_line,default_credit_line,reinstate_credit_line,forgive_debt. - Risk parameters:
update_risk_parameters,set_rate_formula_config/clear_rate_formula_config,set_rate_change_limits,set_borrower_rate_floor,set_penalty_surcharge_bps,set_grace_period_config. - Caps & limits:
set_max_draw_amount,set_max_repay_amount,set_draw_min_interval,set_utilization_cap,set_max_total_exposure,set_credit_limit_bounds. - Liquidity & treasury:
set_liquidity_token,set_liquidity_source,set_protocol_fee_bps,set_treasury,withdraw_treasury. - Collateral (optional):
deposit_collateral,withdraw_collateral,partial_release_collateral(borrower-callable; releases a portion of collateral while keeping health-factor ≥MinCollateralRatioBps). - Repayment schedule:
set_repayment_schedule,get_repayment_schedule,is_delinquent. - Operational controls:
pause_protocol/unpause_protocol,freeze_draws/unfreeze_draws,block_borrower/unblock_borrower/bulk_block_borrowers,accrue_batch,reverse_draw. - Auction & oracle:
set_auction_contract,settle_default_liquidation,set_oracle_config. - Upgrade:
upgrade(new_wasm_hash). - Queries: read-only
get_*/enumerate_*/is_*entrypoints.
Auction (#[contract],
gateway-contract/contracts/auction_contract/src/lib.rs):
init_auction(auction_id, mode, start_time, end_time, min_bid, min_increment_bps, dutch_start_price, dutch_floor_price, dutch_decay, dutch_step_count)set_factory_contract(factory)place_bid(auction_id, bidder, amount)— English ascending or Dutch descending mode, with anti-grief minimum increment and reentrancy-guarded refund of the prior bidderclose_auction(auction_id)settle_default_liquidation(auction_id, credit_contract, borrower) -> i128— factory-only, one-shot perauction_idclaim_auction(auction_id)— winner-only
- Credit-line core with
ContractError,DataKey, events; pinned by CI tests. - Risk-pricing formula (
compute_rate_from_score), per-borrower floor, rate-change cap, penalty surcharge, grace policy. - Lazy interest accrual with three branches (current, delinquent, grace).
- English & Dutch auction modes; reentrancy-guarded refunds.
- Cross-contract default-liquidation handoff with two-sided replay protection.
- Oracle deviation & staleness circuit breaker.
- Admin-gated WASM upgrade with schema version bump.
- Circuit breaker (
pause_protocol) with repay-credit exception. - Treasury + protocol fee on interest portion.
- Per-borrower utilization cap, per-borrower exposure cap, global exposure cap, draw cooldown, per-tx caps.
- Collateral as an optional (default-on) floor.
- Borrower self-suspend.
- Storage TTL hygiene with automatic bump on access.
- Integration tests, with line coverage measured and floor-enforced in CI on every run.
- Anti-snipe extension for English auctions (documented in PR #430, not
yet active in
place_bid). - Decentralized default-signal oracle per
docs/default-oracle.md(signed attestation, signer set, nonce replay protection). - Build-clean main — resolve the merge-artifact duplicates in
lifecycle.rsandrisk.rsthat produce the currentcargo checkerrors. - Property-fuzz harness (
cargo fuzz) overapply_accrualandcompute_rate_from_score. - External audit (see
contracts/credit/AUDIT_SUMMARY.md). - Decentralized scorer pipeline — move the off-chain scoring function to a stake-weighted committee or zk-attested compute.
- Edition: 2021. Toolchain: pinned exactly in
rust-toolchain.toml— never build with a floating channel; seescripts/check-toolchain.sh. - Style:
cargo fmt --checkenforced in CI;cargo clippy -- -D warningsenforced in CI. - Errors: no production
unwrap()/expect()(audited, PR #418 / #421). Every fallible path returns aContractError. - ABI stability:
ContractErrordiscriminants are pinned bytests/error_discriminants.rs; event topics bytests/event_topic_stability.rs. - Commit style: conventional commits (
docs:,feat:,fix:,security:,chore:,test:). - Branching: feature branches off
main, PRs reviewed and merged via GitHub. - Contributing: See
docs/CONTRIBUTING.mdfor contribution guidelines. PR descriptions, checklists, and temporary write-ups belong in GitHub PRs, not committed to the repository root.
| Script | Use |
|---|---|
scripts/build_wasm.sh [all|credit|auction] |
Build release-mode WASM artifacts (toolchain-pin asserted, --locked) |
scripts/check_workspace.sh [args] |
cargo check --workspace --locked wrapper; asserts the release overflow policy first |
scripts/check-overflow-checks.sh |
Fail when a release profile drops overflow-checks = true |
scripts/check-toolchain.sh [--verify-active] |
Enforce the reproducible-build policy (exact toolchain pin, committed locks, CI workflow consumes the pin) |
scripts/clean_profraw.sh [--dry-run] |
Remove stray *.profraw coverage profiles outside target/ |
scripts/list_contract_errors.py [--json|--categories|--check] |
Print every ContractError variant, or verify docs/errors.md against the enum |
See scripts/README.md for conventions.
See Cargo.toml for crate-level metadata. Both creditra-credit and
gateway-auction carry an SPDX license identifier; SPDX headers are
preserved by CI tests in tests/spdx_header_preservation.rs and
tests/spdx_preservation_standalone.rs.
# Workspace topology
ls contracts/credit/tests/*.rs | wc -l
grep -r '#\[test\]' contracts/ gateway-contract/ | wc -l
git log --oneline | grep -c Merge
# Coverage (the gate CI enforces, from the crate CI actually builds)
cargo install cargo-llvm-cov --version 0.9.1 --locked
(cd contracts/creditra-credit \
&& cargo llvm-cov --all-targets --html --fail-under-lines 92)
# Size budget
cargo build --release --target wasm32-unknown-unknown -p creditra-credit \
&& ls -l target/wasm32-unknown-unknown/release/creditra_credit.wasm
# Error catalog
python3 scripts/list_contract_errors.py --checkFor the long-form protocol description, start with WHITEPAPER.md.