The Remitwise Contracts suite implements a comprehensive financial management system on the Stellar network using Soroban smart contracts. The architecture follows a modular design with clear separation of concerns and integrated data flow.
┌─────────────────┐ ┌─────────────────┐
│ Remittance │────│ Bill Payments │
│ Split │ │ │
│ │ └─────────────────┘
└─────────┬───────┘ │
│ │
│ │
v v
┌─────────────────┐ ┌─────────────────┐
│ Savings Goals │ │ Insurance │
│ │ │ │
└─────────────────┘ └─────────────────┘
│ │
│ │
└──────────┬──────────┘
│
v
┌─────────────────┐
│ Reporting │
│ (Aggregator) │
└─────────────────┘
│
▼
┌─────────────────┐
│ remitwise-common│
│ (Shared Lib) │
└─────────────────┘
The remitwise-common crate provides shared types, enums, and utilities used across multiple contracts:
- Category: Financial allocation categories (Spending, Savings, Bills, Insurance)
- FamilyRole: Access control roles for family wallet management
- CoverageType: Insurance policy coverage types
- EventCategory/EventPriority: Standardized event logging
- Constants: Pagination limits, storage TTL values, batch sizes
- Utilities: Event emission helpers, limit validation functions
Incoming Remittance
│
▼
┌─────────────────┐
│ Remittance Split│ Calculate allocation percentages
│ Contract │ [spending, savings, bills, insurance]
└───────┬─────────┘
│
├─────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│Savings Goals│ │Bill Payments│
│ │ │ │
└─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ Insurance │
│ │
└─────────────┘
Purpose: Central allocation engine for incoming remittances
Key Features:
- Percentage-based fund allocation
- Owner-controlled configuration
- Backward-compatible storage
- Event-driven audit trail
Storage Structure:
Instance Storage:
├── CONFIG: SplitConfig { owner, percentages, initialized }
├── SPLIT: Vec<u32> [spending, savings, bills, insurance]
Relationships:
- Provides: Allocation ratios to other contracts
- Consumes: None (entry point for remittances)
Purpose: Manage recurring and one-time bill payments
Key Features:
- Bill creation with due dates
- Payment tracking and status
- Recurring bill automation
- Overdue bill identification
- Optional external reference IDs for off-chain linkage
Storage Structure:
Instance Storage:
├── BILLS: Map<u32, Bill>
├── NEXT_ID: u32
├── ARCH_BILL: Map<u32, ArchivedBill>
├── STOR_STAT: StorageStats
Relationships:
- Provides: Bill payment tracking
- Consumes: Allocation amounts from Remittance Split
- Integrates: With Insurance for premium bills
Purpose: Insurance policy management and premium tracking
Key Features:
- Policy creation and activation
- Monthly premium scheduling
- Payment tracking
- Policy deactivation
- Optional external reference IDs for off-chain linkage
Storage Structure:
Instance Storage:
├── POLICIES: Map<u32, InsurancePolicy>
├── NEXT_ID: u32
├── ARCH_POL: Map<u32, ArchivedPolicy>
├── STOR_STAT: StorageStats
Relationships:
- Provides: Insurance premium amounts
- Consumes: Allocation amounts from Remittance Split
- Integrates: With Bill Payments for premium tracking
Purpose: Goal-based savings management
Key Features:
- Goal creation with targets
- Fund addition/withdrawal
- Goal locking mechanism
- Progress tracking
Storage Structure:
Instance Storage:
├── GOALS: Map<u32, SavingsGoal>
├── NEXT_ID: u32
├── ARCH_GOAL: Map<u32, ArchivedGoal>
├── STOR_STAT: StorageStats
Relationships:
- Provides: Savings allocation management
- Consumes: Allocation amounts from Remittance Split
Purpose: Cross-contract data aggregation and comprehensive financial reporting
Key Features:
- Cross-contract data queries
- Financial health score calculation
- Multiple report types (remittance, savings, bills, insurance)
- Trend analysis and period comparisons
- Report storage and retrieval
- Category-wise breakdowns
Storage Structure:
Instance Storage:
├── ADMIN: Address
├── ADDRS: ContractAddresses
├── REPORTS: Map<(Address, u64), FinancialHealthReport>
├── ARCH_RPT: Map<(Address, u64), ArchivedReport>
├── STOR_STAT: StorageStats
Relationships:
- Provides: Aggregated financial insights and reports
- Consumes: Data from all other contracts via cross-contract calls
- Integrates: With remittance_split, savings_goals, bill_payments, insurance, family_wallet
fn process_remittance(env: Env, user: Address, amount: i128) {
// 1. Calculate allocations
let allocations = remittance_split::calculate_split(env, amount);
// 2. Allocate to savings
savings_goals::add_to_goal(env, user, primary_goal, allocations[1]);
// 3. Create bill payments
bill_payments::create_bill(env, user, "Monthly Bills", allocations[2], due_date, false, 0, None);
// 4. Pay insurance premiums
insurance::pay_premium(env, user, active_policy);
}fn get_financial_overview(env: Env, user: Address) -> FinancialOverview {
let unpaid_bills = bill_payments::get_total_unpaid(env, user);
let monthly_premium = insurance::get_total_monthly_premium(env, user);
let savings_goals = savings_goals::get_all_goals(env, user);
let split_config = remittance_split::get_config(env);
FinancialOverview {
unpaid_bills,
monthly_premium,
savings_goals,
split_config,
}
}The Reporting contract aggregates data from all contracts:
fn generate_financial_health_report(env: Env, user: Address) -> FinancialHealthReport {
// Query remittance split configuration
let split_client = RemittanceSplitClient::new(&env, &split_address);
let split_config = split_client.get_split(&env);
// Query savings progress
let savings_client = SavingsGoalsClient::new(&env, &savings_address);
let goals = savings_client.get_all_goals(user.clone());
// Query bill compliance
let bill_client = BillPaymentsClient::new(&env, &bills_address);
let unpaid_bills = bill_client.get_unpaid_bills(user.clone());
// Query insurance coverage
let insurance_client = InsuranceClient::new(&env, &insurance_address);
let policies = insurance_client.get_active_policies(user);
// Calculate health score and generate report
calculate_health_score_and_report(split_config, goals, unpaid_bills, policies)
}- Owner Authorization: All operations require owner signature
- Contract Isolation: Each user has isolated data
- Input Validation: Comprehensive parameter validation
- State Consistency: Atomic operations prevent inconsistent states
- TTL Management: Automatic storage cleanup
- Instance Storage: Efficient data organization
- Event Logging: Complete audit trail
- Panic Handling: Fail-fast on invalid operations
All event topics and storage keys follow standardized naming conventions documented in:
- Full Conventions:
docs/naming-conventions.md - Quick Reference:
docs/naming-quick-reference.md - Audit & Action Plan:
docs/naming-audit-action-plan.md
Key Principles:
- Event topics: lowercase, max 8 characters, past tense for actions
- Storage keys: UPPERCASE, underscores for multi-word, max 8 characters
- Event enums: PascalCase, descriptive names
Bill Payments Events:
├── Namespace: "bills"
├── BillEvent::Created
├── BillEvent::Paid
├── BillEvent::ExternalRefUpdated
Insurance Events:
├── Namespace: "insure"
├── InsuranceEvent::PolicyCreated
├── InsuranceEvent::PremiumPaid
├── InsuranceEvent::PolicyDeactivated
├── InsuranceEvent::ExternalRefUpdated
Remittance Split Events:
├── Namespace: "split"
├── SplitEvent::Initialized
├── SplitEvent::Updated
├── SplitEvent::Calculated
Savings Goals Events:
├── Namespace: "savings"
├── SavingsEvent::GoalCreated
├── SavingsEvent::FundsAdded
├── SavingsEvent::FundsWithdrawn
├── SavingsEvent::GoalCompleted
├── SavingsEvent::GoalLocked
├── SavingsEvent::GoalUnlocked
Reporting Events:
├── Namespace: "report"
├── ReportEvent::ReportGenerated
├── ReportEvent::ReportStored
├── ReportEvent::AddressesConfigured
User Action → Contract Function → State Change → Event Emission → Off-chain Processing
// Standard pattern used across all contracts
env.events().publish(
(symbol_short!("<namespace>"), EventEnum::Variant),
event_data
);
// Example from Savings Goals
env.events().publish(
(symbol_short!("savings"), SavingsEvent::GoalCreated),
(goal_id, owner)
);- Instance Storage: Used for frequently accessed data
- TTL Extension: Prevents storage bloat
- Efficient Maps: O(1) access patterns
- Minimal Data Duplication: Shared storage keys
- Tiered TTL Strategy: Active data (~30 days), archived data (~180 days)
- Data Archival: Completed/inactive records moved to compressed archive storage
- Bulk Cleanup: Functions to permanently delete old archives
- Storage Monitoring:
StorageStatsstruct tracks active/archived counts in all contracts
Each contract implements a comprehensive archival system:
Active Storage Archive Storage
├── GOALS (Map<u32, SavingsGoal>) → ARCH_GOAL (Map<u32, ArchivedGoal>)
├── BILLS (Map<u32, Bill>) → ARCH_BILL (Map<u32, ArchivedBill>)
├── POLICIES (Map<u32, Policy>) → ARCH_POL (Map<u32, ArchivedPolicy>)
├── PEND_TXS (Map<u64, PendingTx>) → ARCH_TX (Map<u64, ArchivedTransaction>)
└── REPORTS (Map<(Addr,u64), Rpt>) → ARCH_RPT (Map<(Addr,u64), ArchivedReport>)
Archival Flow:
- Archive functions move completed/inactive records to archive storage
- Archived records use compressed structs with essential fields only
- Archive storage uses longer TTL (6 months) for cost efficiency
- Cleanup functions permanently delete old archives when no longer needed
- Restore functions can move archived records back to active storage
Archived Data Compression:
ArchivedGoal: Removeslocked,target_date(no longer relevant)ArchivedBill: Removesdue_date,recurring,frequency_days,created_atArchivedPolicy: Removesmonthly_premium,coverage_amount,next_payment_dateArchivedTransaction: Stores onlytx_id,tx_type,proposer, timestampsArchivedReport: Stores onlyuser,period_key,health_score, timestamps
- Batch Operations: Minimize cross-contract calls
- Caching: Client-side caching of configurations
- Pagination: For large result sets
- Async Processing: Event-driven architecture
Contracts using monotonic u32 IDs:
bill_payments:BILLSmap +NEXT_IDinsurance:POLICIESmap +NEXT_IDsavings_goals:GOALSmap +NEXT_ID
Current create paths use NEXT_ID + 1. At u32::MAX (4,294,967,295), the next create attempt overflows and reverts.
Overflow behavior rationale:
- Repository release profile sets
overflow-checks = truein rootCargo.toml. - With overflow checks enabled, increment overflow traps/reverts rather than silently wrapping.
- Operationally, this is safer than wraparound, but still creates a hard stop for new records once max ID is reached.
Although u32 allows billions of IDs, practical limits are much lower because several read methods scan 1..=NEXT_ID:
bill_payments:get_unpaid_bills,get_overdue_bills,get_all_billsinsurance:get_active_policiessavings_goals:get_all_goals
Recommended operational caps:
| Contract | Per-user recommended max | Per-contract recommended max (NEXT_ID) |
Rationale |
|---|---|---|---|
bill_payments |
2,000 bills/owner | 20,000 | Multiple scan-heavy reads; canceled bills leave ID gaps so scan cost still tracks NEXT_ID. |
insurance |
500 policies/owner | 15,000 | Active-policy queries scan full ID range; deactivated policies still consume IDs. |
savings_goals |
1,000 goals/owner | 20,000 | Owner list path scans full ID range. |
family_wallet* |
50 members, 500 pending tx | N/A (u64 tx IDs) |
Numeric overflow is not a practical concern; cap to control operational complexity. |
* family_wallet transaction IDs are u64 (NEXT_TX), not u32.
Track per deployed contract:
NEXT_IDvalue and growth rate.NEXT_IDvs active records (gap ratio), especially where cancellations/removals exist.- Per-owner record distribution (top-N owners) from off-chain indexing.
- Failure/latency trends for scan-heavy read methods.
- Create-path revert rates, especially near caps.
Alert thresholds:
- Warning: 70% of recommended contract max.
- Critical: 90% of recommended contract max.
- Absolute overflow safety warning:
NEXT_ID >= 3,500,000,000.
- Apply off-chain admission control for new creates (global and per-owner caps).
- Route new records to a fresh deployment shard before query performance degrades.
- Prioritize migration of highest-volume owners.
- Plan API/storage evolution (pagination and owner-indexed iteration) before raising caps.
Contract Function
├── Success → Return Result
├── Validation Error → Panic with message
├── Access Error → Panic with message
└── Storage Error → Panic with message
- Bill Payments: BillNotFound, BillAlreadyPaid, InvalidAmount, etc.
- Insurance: Policy not found, unauthorized, inactive policy
- Remittance Split: Invalid percentages, not initialized
- Savings Goals: Goal not found, insufficient balance, locked goal
Each contract includes comprehensive unit tests covering:
- Happy path scenarios
- Error conditions
- Edge cases
- Integration scenarios
Cross-contract functionality testing:
- Remittance processing workflows
- Multi-contract state consistency
- Event emission verification
- Access control validation
Development → Testnet → Mainnet
│ │ │
├── Unit Tests │
└── Integration Tests└── Production Monitoring
Remittance Split ← Bill Payments
Remittance Split ← Insurance
Remittance Split ← Savings Goals
All Contracts ← Reporting (read-only queries)
No circular dependencies ensure clean deployment order. The Reporting contract should be deployed last after all other contracts are deployed and their addresses are known.
- Multi-currency support
- Advanced scheduling
- Automated payments
- Reporting dashboards
- Third-party integrations
- Plugin system for custom allocation rules
- Sub-contracts for specialized functionality
- Cross-chain bridges for multi-network support
- Governance mechanisms for protocol upgrades