Decentralized Real Estate Finance on Stellar · Built with Soroban
╔═══════════════════════════════════════════════════════════════╗
║ Tokenize property. Fractionalize ownership. ║
║ Stream rent. Borrow against equity. Go cross-border. ║
║ Stay compliant — without putting PII on-chain. ║
╚═══════════════════════════════════════════════════════════════╝
PropFi is a full-stack, production-grade decentralized real estate protocol on the Stellar blockchain. It lets anyone — anywhere — tokenize a property, split it into tradeable fractions, stream rental income to investors, and take out on-chain mortgages against equity. Every operation is gated by zero-knowledge KYC attestations, jurisdiction-aware compliance rules, and governed by fraction holders through on-chain voting.
No banks. No brokers. No paper. Just code.
- Why PropFi
- Protocol Overview
- Architecture
- Smart Contracts
- User Flow
- Tech Stack
- Project Structure
- Getting Started
- Running Tests
- Deployment
- Frontend
- Compliance & Privacy
- Governance
- Roadmap
- Contributing
- License
Real estate is the world's largest asset class — $326 trillion — yet it remains one of the least accessible. High entry costs, opaque pricing, slow settlement, and broken cross-border payment rails lock out the majority of the world's population.
PropFi fixes this at the protocol layer:
| Problem | PropFi Solution |
|---|---|
| Minimum $100k+ investment | Fractional ownership from $10 |
| Settlement takes weeks | On-chain, finalized in seconds |
| Rent lost to intermediaries | Automated pro-rata streaming |
| No credit access for property owners | Permissionless on-chain mortgages |
| Cross-border payments expensive & slow | Stellar path payments + SEP-24/31 |
| KYC processes expose personal data | Zero-knowledge attestation proofs |
| Protocol changes by committee | On-chain fraction-holder governance |
┌─────────────────────────────────────────┐
│ PropFi Protocol │
└─────────────────────────────────────────┘
│
┌───────────────────────────────┼──────────────────────────────┐
│ │ │
┌───────▼──────┐ ┌──────────▼──────────┐ ┌─────────▼──────┐
│ RWA Layer │ │ DeFi Layer │ │ Payments Layer │
│ │ │ │ │ │
│ PropertyReg │◄────────────│ FractionVault │ │ RentDistributor│
│ OracleAdapter│ │ MortgagePool │ │ PaymentBridge │
└──────────────┘ └──────────────────────┘ └────────────────┘
│ │ │
└───────────────────────────────┼──────────────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Compliance & Governance │
│ ComplianceRegistry · Governance │
└─────────────────────────────────────────┘
PropFi is composed of 8 Soroban smart contracts, each with a distinct responsibility, communicating through cross-contract calls. All contracts share a common compliance gate via ComplianceRegistry.
ComplianceRegistry ◄─────────────────────────────────────── (all contracts)
│
PropertyRegistry ◄────── FractionVault ◄────── RentDistributor ◄── PaymentBridge
│ │ │
└──────► OracleAdapter ◄┘ │
│ │
MortgagePool ◄─────────────────────────┘
│
Governance ◄───── FractionVault (voting power)
The canonical RWA layer. Tokenizes real-world properties as on-chain assets with oracle-fed valuations and legally-binding document hashes.
pub fn register_property(env: Env, owner: Address, valuation: i128, doc_hash: BytesN<32>, jurisdiction: Symbol) -> u64
pub fn update_valuation(env: Env, prop_id: u64, new_val: i128, oracle_contract: Address, asset: Symbol)
pub fn transfer_ownership(env: Env, prop_id: u64, to: Address, compliance_contract: Address)
pub fn get_property(env: Env, prop_id: u64) -> PropertyData
pub fn set_status(env: Env, prop_id: u64, status: PropertyStatus)
pub fn get_property_jurisdiction(env: Env, prop_id: u64) -> SymbolEvents: PropertyRegistered · ValuationUpdated · OwnershipTransferred
Splits a tokenized property into tradeable ERC-1155-style fraction tokens. Powers fractional ownership and secondary market trading.
pub fn initialize(env: Env, admin: Address)
pub fn fractionalize(env: Env, prop_id: u64, total_supply: u128, price: i128, payment_token: Address, property_registry: Address, compliance_registry: Address)
pub fn buy_fraction(env: Env, buyer: Address, prop_id: u64, amount: u128)
pub fn sell_fraction(env: Env, seller: Address, prop_id: u64, amount: u128, min_price: i128)
pub fn get_balance(env: Env, investor: Address, prop_id: u64) -> u128
pub fn total_holders(env: Env, prop_id: u64) -> u32
pub fn get_fraction_info(env: Env, prop_id: u64) -> (u128, i128, Address, Address, Address)
pub fn set_rent_distributor(env: Env, distributor: Address)Events: Fractionalized · FractionPurchased · FractionSold
Streams and distributes rent payments proportionally across all fraction holders. Supports XLM and USDC, batch payouts, and configurable distribution schedules.
pub fn initialize(env: Env, admin: Address)
pub fn deposit_rent(env: Env, sender: Address, prop_id: u64, amount: i128, token: Address)
pub fn distribute(env: Env, prop_id: u64)
pub fn claim(env: Env, prop_id: u64, investor: Address)
pub fn set_schedule(env: Env, prop_id: u64, interval_days: u32)
pub fn set_fraction_vault(env: Env, vault: Address)
pub fn pending_yield(env: Env, investor: Address, prop_id: u64) -> i128
pub fn checkpoint(env: Env, caller: Address, investor: Address, prop_id: u64, balance: u128)Events: RentDeposited · YieldDistributed · YieldClaimed
Permissionless on-chain lending. Property owners borrow against tokenized equity; lenders supply liquidity and earn interest. LTV-gated with automated liquidation triggers.
pub fn open_loan(env: Env, borrower: Address, prop_id: u64, amount: i128) -> u64
pub fn repay(env: Env, borrower: Address, loan_id: u64, amount: i128)
pub fn liquidate(env: Env, liquidator: Address, loan_id: u64)
pub fn deposit_liquidity(env: Env, lp: Address, amount: i128)
pub fn withdraw_liquidity(env: Env, lp: Address, amount: i128)
pub fn loan_health(env: Env, loan_id: u64) -> HealthFactorMaximum LTV: 70%. Liquidation threshold: 80%. Interest accrues per ledger.
Events: LoanOpened · Repaid · Liquidated · LiquidityDeposited
Cross-border payment and remittance layer. Wraps Stellar's native path payments for multi-currency collection and payout. SEP-24 and SEP-31 anchor integration for fiat on/off-ramps.
pub fn deposit(env: Env, user: Address, asset: Symbol, amount: i128)
pub fn withdraw(env: Env, user: Address, asset: Symbol, amount: i128)
pub fn send(env: Env, from: Address, to: Address, amount: i128, src: Symbol, dst: Symbol)
pub fn batch_send(env: Env, from: Address, recipients: Vec<(Address, i128)>, src: Symbol, dst: Symbol)
pub fn register_anchor(env: Env, asset: Symbol, token_address: Address)
pub fn estimate_path(env: Env, src: Symbol, dst: Symbol, amount: i128) -> PathQuoteEvents: PaymentSent · BatchDispatched · AnchorRegistered
Multi-source price aggregation with TWAP support. Feeds property valuations to PropertyRegistry and LTV calculations to MortgagePool. Staleness detection built in.
pub fn submit_price(env: Env, oracle: Address, asset: Symbol, price: i128)
pub fn get_price(env: Env, asset: Symbol) -> PriceData
pub fn add_oracle(env: Env, oracle_addr: Address, weight: u32)
pub fn remove_oracle(env: Env, oracle_addr: Address)
pub fn twap(env: Env, asset: Symbol, window_secs: u64) -> i128
pub fn get_oracle_info(env: Env, oracle_addr: Address) -> OracleInfoEvents: PriceUpdated · OracleAdded · StaleAlert
Zero-knowledge KYC/AML attestation layer. Stores proof hashes — never raw PII. Issues compliance credentials consumed by all other contracts. Jurisdiction-aware rule configuration.
pub fn initialize(env: Env, admin: Address)
pub fn attest(env: Env, user: Address, proof_hash: Bytes, jurisdiction: Symbol, duration_days: u32)
pub fn is_compliant(env: Env, user: Address, jurisdiction: Symbol) -> bool
pub fn revoke(env: Env, user: Address)
pub fn set_jurisdiction_rules(env: Env, jurisdiction: Symbol, rules: JurisdictionRules)
pub fn attestation_expiry(env: Env, user: Address) -> u64Events: Attested · Revoked · RulesUpdated
On-chain protocol governance. Proposals are voted on by fraction holders; vote weight is proportional to aggregate holdings. Timelock-enforced execution.
pub fn initialize(env: Env, admin: Address, fraction_vault: Address)
pub fn set_quorum(env: Env, quorum: u128)
pub fn add_tracked_property(env: Env, prop_id: u64)
pub fn propose(env: Env, action_type: u32, calldata: Bytes, description: String) -> u64
pub fn vote(env: Env, voter: Address, proposal_id: u64, support: bool)
pub fn execute(env: Env, proposal_id: u64)
pub fn voting_power(env: Env, user: Address) -> u128
pub fn get_proposal(env: Env, proposal_id: u64) -> ProposalDataTimelock: 48 hours. Quorum: 10% of total fraction supply.
Events: ProposalCreated · Voted · ProposalExecuted
USER PROPFI PROTOCOL STELLAR NETWORK
│ │ │
├─── submit KYC proof ────────►│ ComplianceRegistry.attest() │
│◄── compliance credential ───┤ │
│ │ │
├─── register property ───────►│ PropertyRegistry.register() │
│ (doc_hash, valuation) │ └─► OracleAdapter.get_price() │
│◄── property token ID ───────┤ │
│ │ │
├─── fractionalize ───────────►│ FractionVault.fractionalize() │
│ (1000 fractions @ $100) │ └─► PropertyRegistry (check) │
│ │ │
├─── investor buys fraction ──►│ FractionVault.buy_fraction() │
│ │ └─► ComplianceRegistry (gate) │
│ │ └─► Stellar token transfer ────►│
│ │ │
├─── tenant pays rent ────────►│ RentDistributor.deposit_rent() │
│ │ └─► PaymentBridge (FX) ─────►│ path payment
│ │ └─► distribute() to holders │
│ │ │
├─── owner borrows ───────────►│ MortgagePool.open_loan() │
│ (against equity) │ └─► OracleAdapter (LTV check) │
│◄── USDC disbursed ──────────┤ └─► PropertyRegistry │
│ │ │
├─── governance vote ─────────►│ Governance.vote() │
│ │ └─► FractionVault (power) │
| Layer | Technology |
|---|---|
| Smart contracts | Rust · Soroban SDK 21.x · stellar-xdr |
| Testing | Soroban test harness · cargo test |
| Contract CLI | soroban-cli |
| Indexer | Stellar Horizon API · Event stream · PostgreSQL |
| Backend SDK | Node.js · TypeScript · stellar-sdk · Prisma |
| Frontend | Next.js 14 · Freighter Wallet · shadcn/ui · TailwindCSS |
| Compliance | ZK attestation proofs · Verifiable Credentials · W3C DID |
| Auth | Stellar keypairs · Freighter · WalletConnect |
| Infrastructure | Docker · Docker Compose · GitHub Actions |
| Network | Stellar Testnet (dev) · Stellar Mainnet (prod) |
propfi/
├── contracts/ # All Soroban smart contracts
│ ├── property_registry/
│ │ ├── src/lib.rs
│ │ └── Cargo.toml
│ ├── fraction_vault/
│ │ ├── src/lib.rs
│ │ └── Cargo.toml
│ ├── rent_distributor/
│ │ ├── src/lib.rs
│ │ └── Cargo.toml
│ ├── mortgage_pool/
│ │ ├── src/lib.rs
│ │ └── Cargo.toml
│ ├── payment_bridge/
│ │ ├── src/lib.rs
│ │ └── Cargo.toml
│ ├── oracle_adapter/
│ │ ├── src/lib.rs
│ │ └── Cargo.toml
│ ├── compliance_registry/
│ │ ├── src/lib.rs
│ │ └── Cargo.toml
│ ├── governance/
│ ├── src/lib.rs
│ └── Cargo.toml
│ └── integration_tests/ # Cross-contract integration tests
│
├── sdk/ # TypeScript client SDK
│ ├── src/
│ │ ├── clients/ # Auto-generated contract clients
│ │ ├── types/ # Shared TypeScript types
│ │ └── index.ts
│ ├── package.json
│ └── tsconfig.json
│
├── indexer/ # Horizon event indexer
│ ├── src/
│ │ ├── handlers/ # Per-contract event handlers
│ │ ├── db/ # Prisma schema + migrations
│ │ └── index.ts
│ └── package.json
│
├── frontend/ # Next.js dApp
│ ├── src/
│ │ ├── app/
│ │ │ ├── dashboard/
│ │ │ ├── properties/
│ │ │ └── compliance/
│ │ ├── components/
│ │ │ └── ui/ # shadcn/ui components
│ │ ├── lib/ # SDK integration, utilities
│ │ └── styles/
│ └── package.json
│
├── scripts/ # Deployment & setup scripts
│ ├── deploy.sh
│ ├── setup_testnet.sh
│ └── seed_data.ts
│
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── deploy.yml
│
├── Cargo.toml # Workspace manifest
├── Cargo.lock
├── docker-compose.yml
└── README.md
- Rust (stable, 1.75+)
- soroban-cli v21.x
- Node.js 20+
- Docker (optional, for local Stellar node)
git clone https://github.com/your-org/propfi.git
cd propfirustup target add wasm32-unknown-unknowncargo build --target wasm32-unknown-unknown --releaseOr build a specific contract:
cd contracts/property_registry
cargo build --target wasm32-unknown-unknown --releasecp .env.example .envEdit .env:
STELLAR_NETWORK=testnet
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
ADMIN_SECRET_KEY=S...
ORACLE_SECRET_KEY=S...
DATABASE_URL=postgresql://localhost:5432/propfisoroban keys generate --global admin --network testnet
soroban keys fund admin --network testnet./scripts/deploy.sh --network testnetThis will deploy all 8 contracts in dependency order, write contract IDs to deployed.json, and initialize the protocol.
# Indexer
cd indexer && npm install && npm run dev
# Frontend
cd frontend && npm install && npm run devcargo test -p property_registry
cargo test -p fraction_vault
cargo test -p mortgage_pool
# ... etccargo test --workspacecargo test -p propfi-integration-testsIntegration tests deploy all contracts to a local environment and run full protocol flows end-to-end.
cargo tarpaulin --workspace --out Html
open tarpaulin-report.html./scripts/deploy.sh --network testnet./scripts/deploy.sh --network mainnet --confirm
⚠️ Mainnet deployment requires a multisig admin keypair. SetMULTISIG_SIGNERSin.envbefore deploying.
PropFi contracts support upgradeability via Soroban's update_current_contract_wasm. Only callable by the Governance contract after a successful proposal vote.
soroban contract invoke \
--id $GOVERNANCE_CONTRACT_ID \
--source admin \
--network testnet \
-- execute \
--proposal_id 42The PropFi dApp is a Next.js 14 application with Freighter wallet integration.
Key pages:
| Route | Description |
|---|---|
/dashboard |
Portfolio overview — holdings, yield, loans |
/properties |
Browse and search tokenized properties |
/compliance |
KYC attestation and credential management |
import { createPropFi } from "@propfi/sdk";
const propfi = createPropFi({
rpcUrl: "https://soroban-testnet.stellar.org",
complianceRegistryId: "C...",
propertyRegistryId: "C...",
fractionVaultId: "C...",
mortgagePoolId: "C...",
});
// Read a property
const property = await propfi.propertyRegistry.getProperty(1);
// Check compliance
const ok = await propfi.complianceRegistry.isCompliant("G...", "US");
// Submit a transaction with Freighter
const signer = {
async getPublicKey() {
return window.freighter.getPublicKey().then((r) => r.publicKey);
},
async signTransaction(txXdr: string) {
return window.freighter
.signTransaction(txXdr, {
networkPassphrase: "Test SDF Network ; September 2015",
})
.then((r) => r.signedTxXdr);
},
};
await propfi.fractionVault.buyFraction("G...", 1, 10n, signer);PropFi implements zero-knowledge KYC attestations — the protocol never stores names, IDs, or documents on-chain. Instead:
- Users complete KYC off-chain with a trusted attestor.
- The attestor issues a signed ZK proof of compliance.
ComplianceRegistry.attest()stores only the proof hash + jurisdiction + expiry.- All sensitive operations check
is_compliant()before execution.
Supported jurisdictions: US, EU, NG, ZA, AE, SG, GB (configurable via governance).
Attestation expiry: 12 months by default. Renewal required before operations resume.
AML: Attestations can be revoked by the compliance admin (a multisig DAO) in response to flagged activity. Revocation immediately gates all contract interactions for the affected address.
PropFi is governed by its fraction holders. Every address holding fractions of any PropFi property has voting power proportional to their total holdings.
- Maximum LTV ratio
- Liquidation threshold
- Distribution schedule defaults
- Oracle weights and trusted sources
- Jurisdiction rules
- Protocol fee rates
- New contract deployments and upgrades
PROPOSED → VOTING (48h) → QUEUED (24h timelock) → EXECUTED
└──► DEFEATED (quorum not met)
Submit a proposal:
soroban contract invoke \
--id $GOVERNANCE_CONTRACT_ID \
--source proposer \
--network testnet \
-- propose \
--action '{"UpdateLTV":{"new_max":6500}}' \
--description "Reduce max LTV to 65% to improve protocol safety"- Core contracts (PropertyRegistry, FractionVault, RentDistributor)
- MortgagePool with LTV and liquidation
- PaymentBridge with SEP-24 anchor support
- OracleAdapter with TWAP
- ComplianceRegistry with ZK attestations
- Governance with timelock
- Mobile app (React Native + Freighter Mobile)
- Secondary market AMM for fractions
- Cross-chain bridge (Ethereum ↔ Stellar)
- Automated rental income staking
- Property insurance pool
- Tokenized REIT baskets
- Layer-2 payment channels for micro-rent
Contributions are welcome. Please read CONTRIBUTING.md before submitting a PR.
- Fork the repo
- Create a feature branch:
git checkout -b feat/my-feature - Write tests for any new contract logic
- Ensure
cargo test --workspacepasses - Submit a PR with a clear description
For significant changes, open an issue first to discuss the approach.
PropFi contracts have not yet undergone a formal security audit. Do not use on mainnet with real funds until an audit is complete.
To report a vulnerability, email security@propfi.xyz — please do not open a public GitHub issue.
MIT © 2024 PropFi Contributors. See LICENSE for details.