From c10285633fd5fe19dff48ee80b3188711d5589e6 Mon Sep 17 00:00:00 2001 From: Dabira Olaoluwa Date: Thu, 20 Aug 2026 23:43:11 -0700 Subject: [PATCH 1/2] docs: consolidate contract documentation into CONTRACTS.md --- AUDIT_READY_RUNBOOK.md | 622 - CONTRACTS.md | 10824 ++++++++++++++++ README.md | 191 - SECURITY.md | 101 - SETTLEMENT_IMPLEMENTATION.md | 179 - contracts/docs/security/macro-auth.md | 78 - contracts/docs/specs/mint-atomicity.md | 76 - .../docs/specs/reconciliation-scaling.md | 80 - contracts/docs/specs/tariff-precision.md | 92 - contracts/meter-aggregator/README.md | 68 - .../resource-token/IMPLEMENTATION_SUMMARY.md | 232 - contracts/resource-token/README.md | 192 - contracts/utility_contracts/src/ERRORS.md | 17 - .../src/GASLESS_RELAY_DOCS.md | 276 - docs/STREAM_INSURANCE_POOL_GOVERNANCE.md | 301 - docs/UTILITY_ERRORS.md | 38 - 16 files changed, 10824 insertions(+), 2543 deletions(-) delete mode 100644 AUDIT_READY_RUNBOOK.md create mode 100644 CONTRACTS.md delete mode 100644 README.md delete mode 100644 SECURITY.md delete mode 100644 SETTLEMENT_IMPLEMENTATION.md delete mode 100644 contracts/docs/security/macro-auth.md delete mode 100644 contracts/docs/specs/mint-atomicity.md delete mode 100644 contracts/docs/specs/reconciliation-scaling.md delete mode 100644 contracts/docs/specs/tariff-precision.md delete mode 100644 contracts/meter-aggregator/README.md delete mode 100644 contracts/resource-token/IMPLEMENTATION_SUMMARY.md delete mode 100644 contracts/resource-token/README.md delete mode 100644 contracts/utility_contracts/src/ERRORS.md delete mode 100644 contracts/utility_contracts/src/GASLESS_RELAY_DOCS.md delete mode 100644 docs/STREAM_INSURANCE_POOL_GOVERNANCE.md delete mode 100644 docs/UTILITY_ERRORS.md diff --git a/AUDIT_READY_RUNBOOK.md b/AUDIT_READY_RUNBOOK.md deleted file mode 100644 index 9776cab..0000000 --- a/AUDIT_READY_RUNBOOK.md +++ /dev/null @@ -1,622 +0,0 @@ -# Audit-Ready Runbook — Utility-Protocol Contracts - -**Contract ID (Testnet):** `CB7PSJZALNWNX7NLOAM6LOEL4OJZMFPQZJMIYO522ZSACYWXTZIDEDSS` -**Network:** Stellar Testnet — replace `--network testnet` with `--network mainnet` for production -**Last updated:** 2026-04-28 -**Classification:** CONFIDENTIAL — DAO Core Team Only -**Audit Status:** ✅ Ready for Zealynx Security Audit - ---- - -## Table of Contents - -1. [Executive Summary](#1-executive-summary) -2. [Security Architecture Overview](#2-security-architecture-overview) -3. [Roles and Responsibilities](#3-roles-and-responsibilities) -4. [Pre-Incident Checklist](#4-pre-incident-checklist) -5. [Scenario A — Active Exploit / Hack in Progress](#5-scenario-a--active-exploit--hack-in-progress) -6. [Scenario B — Protocol Pause (Planned or Precautionary)](#6-scenario-b--protocol-pause-planned-or-precautionary) -7. [Scenario C — Wasm Hash Upgrade](#7-scenario-c--wasm-hash-upgrade) -8. [Scenario D — Migrating Trapped State](#8-scenario-d--migrating-trapped-state) -9. [Scenario E — Multi-Sig Withdrawal Freeze](#9-scenario-e--multi-sig-withdrawal-freeze) -10. [Scenario F — Legal Freeze](#10-scenario-f--legal-freeze) -11. [Scenario G — Gas Buffer Exhaustion](#11-scenario-g--gas-buffer-exhaustion) -12. [Scenario H — Admin Key Compromise](#12-scenario-h--admin-key-compromise) -13. [Scenario I — Oracle Failure](#13-scenario-i--oracle-failure) -14. [Scenario J — Velocity Limit Breach / Flash Drain](#14-scenario-j--velocity-limit-breach--flash-drain) -15. [Scenario K — Nonce Desync Attack (New)](#15-scenario-k--nonce-desync-attack-new) -16. [Scenario L — Tariff Oracle Compromise (New)](#16-scenario-l--tariff-oracle-compromise-new) -17. [Scenario M — Ghost Stream Cleanup (New)](#17-scenario-m--ghost-stream-cleanup-new) -18. [Post-Incident Procedures](#18-post-incident-procedures) -19. [Multi-Sig Signer Reference Card](#19-multi-sig-signer-reference-card) -20. [Contact Tree](#20-contact-tree) -21. [Audit Checklist](#21-audit-checklist) - ---- - -## 1. Executive Summary - -The Utility-Protocol Contracts platform provides a decentralized utility streaming protocol with comprehensive security measures including: - -- **Tamper-proof nonce synchronization** for IoT device liveness verification -- **Time-of-Use tariff pricing** with 24-hour schedules -- **Automated ghost stream cleanup** to maintain ledger efficiency -- **Multi-sig governance** for critical operations -- **Emergency response capabilities** for rapid threat mitigation - -### Security Improvements Implemented (Issues #260-263) - -| Issue | Feature | Security Benefit | -|-------|---------|------------------| -| #260 | Hardware Nonce Sync | Eliminates replay attacks against device liveness monitoring | -| #261 | Utility-Tariff Oracle | Enables complex pricing models with seamless rate transitions | -| #262 | Ghost Stream Sweeper | Reduces ledger footprint while maintaining historical integrity | -| #263 | Documentation Sweep | Enterprise-grade documentation for audit readiness | -| #68 | Kafka Lag Monitor & Scaler | System-wide real-time Kafka consumer lag monitoring and auto-scaling | - ---- - -## 2. Security Architecture Overview - -### 2.1 Core Security Components - -#### Nonce Synchronization System -- **Purpose:** Prevent replay attacks on IoT device heartbeats -- **Implementation:** Strict incrementing u64 nonce per device MAC address -- **Security Features:** - - +1 to +5 nonce window for network jitter tolerance - - Multi-sig nonce reset for compromised devices - - Automatic suspicious device marking - - Comprehensive audit trail - -#### Tariff Oracle System -- **Purpose:** Manage Time-of-Use pricing schedules -- **Implementation:** 24-hour pricing windows with grid administrator control -- **Security Features:** - - 24-hour notice period for tariff changes - - Cryptographic signature verification - - Temporary storage optimization - - Seamless rate interpolation - -#### Ghost Stream Management -- **Purpose:** Maintain ledger efficiency by pruning abandoned streams -- **Implementation:** 90-day zero balance threshold with archive preservation -- **Security Features:** - - Cryptographic archive hashes for integrity - - Gas bounty incentives for relayers - - Protection for streams with pending buffers - - Historical audit trail preservation - -### 2.2 Threat Model Coverage - -| Threat Vector | Mitigation | Implementation | -|--------------|------------|----------------| -| Replay Attacks | Nonce synchronization | Issue #260 | -| Price Manipulation | Signed tariff updates | Issue #261 | -| Ledger Bloat | Automated cleanup | Issue #262 | -| Insider Threats | Multi-sig controls | Existing | -| Smart Contract Bugs | Comprehensive testing | Issue #263 | - ---- - -## 3. Roles and Responsibilities - -| Role | On-chain Key / Storage | Duty | New Security Features | -|---|---|---|---| -| **DAO Admin** | `DataKey::CurrentAdmin` | Propose/finalize Wasm upgrades, set compliance officer, grant provider verification, set velocity limits | Tariff oracle admin, Nonce reset authorization | -| **Compliance Officer** | `DataKey::ComplianceOfficer` | Trigger and release legal freezes | Ghost stream emergency cleanup | -| **Finance Wallet (×3–5)** | `MultiSigConfig.finance_wallets` | Propose, approve, revoke, and cancel large withdrawal requests; quorum = `required_signatures` | Ghost stream gas bounty approval | -| **Oracle / Resolver** | `DataKey::Oracle` | Resolve service challenges (`resolve_challenge`) | Tariff oracle signing | -| **Grid Administrator** | `DataKey::TariffOracleAdmin` | Manage tariff schedules | **New** - Issue #261 | -| **Nonce Reset Authority** | `DataKey::AuthorizedNonceResetters` | Reset compromised device nonces | **New** - Issue #260 | -| **Provider** | Per-meter `provider` field | Pause/shutdown individual meters, initiate firmware updates, manage gas buffer | Device nonce management | -| **Ghost Sweeper** | Decentralized relayer | Prune abandoned streams | **New** - Issue #262 | -| **Compliance Council** | Off-chain multi-sig (≥2) | Release legal freezes | Emergency tariff overrides | - -### Multi-sig quorum rule - -Any action requiring `required_signatures` approvals **must be coordinated off-chain first** (Signal group, emergency Telegram, or PagerDuty). Confirm quorum is available before submitting the first on-chain transaction. The contract enforces the threshold — a request with insufficient approvals will revert on execution. - -### Key storage locations (for incident verification) - -``` -DataKey::CurrentAdmin → DAO Admin address -DataKey::ComplianceOfficer → Compliance Officer address -DataKey::Oracle → Oracle/Resolver address -DataKey::TariffOracleAdmin → Grid Administrator address (New) -DataKey::MultiSigConfig(addr) → Per-provider multi-sig config -DataKey::VetoDeadline → Active upgrade veto deadline (Unix timestamp) -DataKey::ProposedUpgrade → Active UpgradeProposal struct -DataKey::DeviceNonce(mac) → Device nonce state (New) -DataKey::CurrentTariffSchedule → Active tariff schedule (New) -DataKey::StreamArchive(id) → Pruned stream archive (New) -``` - ---- - -## 4. Pre-Incident Checklist - -Run every check before executing any emergency command. Do not skip steps. - -```bash -# 1. Confirm Stellar CLI is installed and on PATH -stellar --version - -# 2. Confirm you are targeting the correct network -stellar network ls - -# 3. Export the contract address -export CONTRACT=CB7PSJZALNWNX7NLOAM6LOEL4OJZMFPQZJMIYO522ZSACYWXTZIDEDSS - -# 4. Export signing identities for your role -export ADMIN_KEY= -export PROVIDER_KEY= -export FINANCE_KEY= -export GRID_ADMIN_KEY= - -# 5. Verify the contract is responsive -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_count - -# 6. Check the current meter count and note it -export METER_COUNT=$(stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_count) -echo "Total meters: $METER_COUNT" - -# 7. Verify your key matches the expected admin address -stellar keys address $ADMIN_KEY -# Compare output against the address stored in DataKey::CurrentAdmin - -# 8. Check nonce sync system health -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - nonce_sync_health_check - -# 9. Verify tariff oracle configuration -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_tariff_oracle_admin - -# 10. Check ghost stream statistics -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_sweeper_statistics - -# 11. Check block explorer for any anomalous recent transactions -# https://stellar.expert/explorer/testnet/contract/$CONTRACT -``` - -> **If the contract is unresponsive:** The Stellar network may be congested or the contract TTL may have expired. Check https://status.stellar.org and the block explorer before proceeding. - ---- - -## 5. Scenario A — Active Exploit / Hack in Progress - -### Immediate Actions (Execute in Order) - -1. **FREEZE ALL STREAMS** (DAO Admin only) -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $ADMIN_KEY \ - -- \ - emergency_freeze_all_streams -``` - -2. **PAUSE NONCE VERIFICATION** (Grid Admin only) -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $GRID_ADMIN_KEY \ - -- \ - pause_nonce_verification -``` - -3. **LOCK TARIFF ORACLE** (Grid Admin only) -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $GRID_ADMIN_KEY \ - -- \ - emergency_lock_tariff_oracle -``` - -4. **ENABLE ENHANCED MONITORING** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $ADMIN_KEY \ - -- \ - enable_emergency_monitoring -``` - -### Verification Steps -```bash -# Confirm all streams are frozen -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - are_streams_frozen - -# Check nonce verification status -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - is_nonce_verification_active - -# Verify tariff oracle is locked -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - is_tariff_oracle_locked -``` - ---- - -## 15. Scenario K — Nonce Desync Attack (New) - -### Detection Indicators -- Multiple `NonceDesyncAlert` events in short succession -- Devices marked as suspicious -- Replay attack patterns in event logs - -### Response Procedures - -1. **Investigate Attack Pattern** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_nonce_desync_alerts \ - --limit 50 -``` - -2. **Isolate Compromised Devices** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $PROVIDER_KEY \ - -- \ - quarantine_devices_by_mac \ - --mac-list -``` - -3. **Reset Device Nonces** (Multi-sig required) -```bash -# Step 1: Create reset request -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $AUTHORIZED_RESETTER_KEY \ - -- \ - create_nonce_reset_request \ - --meter-id \ - --device-mac \ - --new-nonce 0 - -# Step 2: Get approvals from other authorized resetters -# (Repeat for each required signature) -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $OTHER_RESETTER_KEY \ - -- \ - approve_nonce_reset \ - --proposal-id - -# Step 3: Execute reset (final approver) -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $FINAL_RESETTER_KEY \ - -- \ - execute_nonce_reset \ - --proposal-id -``` - -4. **Update Security Parameters** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $ADMIN_KEY \ - -- \ - update_nonce_security_params \ - --window-size 3 \ - --suspicious-threshold 5 -``` - ---- - -## 16. Scenario L — Tariff Oracle Compromise (New) - -### Detection Indicators -- Invalid tariff rates being applied -- Unauthorized tariff schedule updates -- Grid administrator key compromise - -### Response Procedures - -1. **Immediate Oracle Lockdown** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $ADMIN_KEY \ - -- \ - emergency_lock_tariff_oracle -``` - -2. **Revert to Default Schedule** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $ADMIN_KEY \ - -- \ - revert_to_default_tariff_schedule -``` - -3. **Replace Grid Administrator** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $ADMIN_KEY \ - -- \ - set_tariff_oracle_admin \ - --new-admin -``` - -4. **Audit Recent Tariff Changes** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_tariff_update_history \ - --days 7 -``` - ---- - -## 17. Scenario M — Ghost Stream Cleanup (New) - -### Detection Indicators -- High storage usage on contract -- Many streams with zero balance > 90 days -- Performance degradation - -### Response Procedures - -1. **Assess Cleanup Candidates** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_ghost_stream_candidates \ - --limit 100 -``` - -2. **Authorize Batch Cleanup** (Multi-sig if needed) -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $RELAYER_KEY \ - -- \ - batch_prune_ghost_streams \ - --stream-ids \ - --relayer -``` - -3. **Verify Cleanup Results** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_sweeper_statistics -``` - -4. **Check Archive Integrity** -```bash -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - verify_archive_integrity \ - --stream-id -``` - ---- - -## 17.1 Scenario N — Kafka Consumer Group Lag Spike (New) - -### Detection Indicators -- `Group Lag Alert` (Warning/Critical) triggered in the System Monitoring Suite. -- Large backlog of unprocessed telemetry, delayed settlements, or pending billing updates. - -### Response Procedures -1. **Analyze Lag on Dashboard:** Inspect the **Kafka Lag & Auto-Scaler** tab to identify bottlenecked partitions. -2. **Override Settings:** Securely lift the `MAX_CONSUMERS` ceiling to scale capacity immediately: -```bash -stellar contract invoke --id $CONTRACT --network testnet --source $ADMIN_KEY -- override_kafka_scaler_config --max_consumers 16 --target_lag_per_consumer 250 -``` -3. **Verify Rebalance:** Confirm the `REBALANCE` completes within the 3-second penalty window using the audit logs. - -## 17.2 Scenario O — Kafka Auto-Scaling Actuator Failure (New) - -### Detection Indicators -- `LIMIT_REACHED` logged in event stream with high lag. -- Actuator endpoint reports credential expiration or `503 Service Unavailable`. - -### Response Procedures -1. **Manual Scale Actuation:** Force scaling via docker/kubernetes direct provisioning to handle backlogs. -2. **Prioritize Topics:** Suspend green-grant topics to give maximum CPU resources to the main prepaid billing queue. - ---- - -## 18. Post-Incident Procedures - -### 1. Incident Documentation -- Create detailed incident report -- Document all actions taken -- Preserve event logs and signatures -- Update runbook with lessons learned - -### 2. Security Review -- Conduct root cause analysis -- Review all affected systems -- Update threat model -- Implement additional safeguards - -### 3. Communication -- Notify all stakeholders -- Publish post-mortem (if appropriate) -- Update documentation -- Schedule security review meeting - -### 4. System Recovery -- Gradually restore services -- Monitor for anomalies -- Update monitoring thresholds -- Conduct penetration testing - ---- - -## 19. Multi-Sig Signer Reference Card - -### Grid Administrator (Tariff Oracle) -```bash -# View current admin -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_tariff_oracle_admin - -# Update tariff schedule -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $GRID_ADMIN_KEY \ - -- \ - propose_tariff_update \ - --schedule \ - --signature -``` - -### Nonce Reset Authority -```bash -# View authorized resetters -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - -- \ - get_authorized_nonce_resetters - -# Reset device nonce -stellar contract invoke \ - --id $CONTRACT \ - --network testnet \ - --source $RESETTER_KEY \ - -- \ - reset_device_nonce \ - --meter-id \ - --device-mac \ - --new-nonce -``` - ---- - -## 20. Contact Tree - -``` -Level 1 (Immediate): DAO Admin, Compliance Officer -Level 2 (15 mins): Grid Administrator, Finance Wallets -Level 3 (30 mins): All Providers, Security Team -Level 4 (1 hour): Community, Public Relations -``` - -**Emergency Channels:** -- Signal Group: `utility-protocol-emergency` -- Telegram: `@iotbilling_emergency` -- PagerDuty: `utility-protocol-security` - ---- - -## 21. Audit Checklist - -### ✅ Documentation Requirements -- [ ] All public functions have comprehensive doc-comments -- [ ] All arguments and return values documented -- [ ] All authorized roles explicitly documented -- [ ] Cross-links between modules are perfect -- [ ] No TODO or FIXME comments remain -- [ ] Security considerations documented -- [ ] Error codes and handling documented - -### ✅ Code Quality Standards -- [ ] No hardcoded secrets or credentials -- [ ] All external dependencies audited -- [ ] Input validation on all public functions -- [ ] Proper access control mechanisms -- [ ] Comprehensive test coverage -- [ ] Fuzz testing for critical components -- [ ] Gas optimization where appropriate - -### ✅ Security Verification -- [ ] Replay attack protection implemented -- [ ] Rate limiting and velocity controls -- [ ] Multi-sig requirements for critical operations -- [ ] Emergency pause mechanisms -- [ ] Audit trail preservation -- [ ] Cryptographic integrity verification -- [ ] Key compromise procedures - -### ✅ Operational Readiness -- [ ] Monitoring and alerting configured -- [ ] Backup and recovery procedures -- [ ] Incident response runbook tested -- [ ] Key rotation procedures documented -- [ ] Upgrade and migration procedures -- [ ] Performance benchmarks established - ---- - -## Conclusion - -This runbook provides comprehensive procedures for managing the Utility-Protocol Contracts platform with the new security improvements implemented in Issues #260-263. The platform is now audit-ready with enterprise-grade documentation, comprehensive security measures, and operational procedures that meet the highest standards for decentralized utility management. - -**Next Steps:** -1. Schedule external security audit with Zealynx -2. Conduct penetration testing on new features -3. Perform full-system integration testing -4. Execute mainnet deployment checklist - ---- - -*This document is confidential and intended for authorized personnel only. Do not distribute outside the DAO core team without explicit permission.* diff --git a/CONTRACTS.md b/CONTRACTS.md new file mode 100644 index 0000000..e5d7ca2 --- /dev/null +++ b/CONTRACTS.md @@ -0,0 +1,10824 @@ +# CONTRACTS & ARCHITECTURE SPECIFICATION + +*This document is the canonical source of truth for all smart contracts, consolidating previous specifications, security runbooks, and readmes.* + +--- + +- [1. Overview](#1-overview) +- [2. Contracts Inventory](#2-contracts-inventory) +- [3. Core Contracts & Specifications](#3-core-contracts-&-specifications) +- [4. Protocol Subsystems](#4-protocol-subsystems) +- [5. Security Considerations](#5-security-considerations) + +--- + +## 1. Overview + +### Source: `README.md` + +### Utility-Protocol Contracts + +Soroban smart contracts for a decentralized utility metering and streaming protocol on Stellar. Supports prepaid/postpaid billing, continuous streaming, variable-rate tariffs, gas buffers, ZK-SNARK sensor privacy, multi-sig governance, and emergency response. + +#### Features + +- **Utility Metering** — Track energy/water consumption with precision billing +- **Prepaid & Postpaid Billing** — Both models supported +- **Continuous Streaming** — Real-time balance monitoring with buffer protection +- **Variable Rate Tariffs** — Peak/off-peak pricing (18:00–21:00 UTC at 1.5× rate) +- **Gas Buffer** — Pre-paid XLM buffer ensures withdrawals clear during network congestion +- **ZK-SNARK Privacy** — Groth16 proofs let meters prove usage without revealing raw readings +- **Firmware Update Gate** — Time-limited, cryptographically signed update authorization +- **Multi-Sig Governance** — 3-of-5 finance wallet quorum for large withdrawals +- **Emergency Response** — Circuit breakers, legal freezes, velocity limits, protocol pauses +- **Dust Sweeper** — Prunes fractional remainders from depleted streams +- **Grant Stream** — Conservation goals trigger automatic grant matching +- **Scheduled Backup Verification** — Restore-tested database backups with metrics, alerts, and canary rollout guidance +- **Oracle Aggregation Framework** — Multi-provider oracle aggregation with a Chainlink `AggregatorV3Interface` adapter, median consensus, deviation/staleness validation, graceful fallback, and per-provider health monitoring (`contracts/oracle-aggregator`) + +#### Project Structure + +``` +Utility-contracts/ +├── contracts/ +│ ├── Cargo.toml ### Workspace root +│ ├── utility_contracts/ ### Main contract +│ │ ├── src/lib.rs ### Core implementation +│ │ ├── src/test.rs ### Test suite +│ │ └── Cargo.toml +│ └── price_oracle/ ### Price oracle contract +├── webhook-delivery-service/ ### High-performance off-chain Webhook service with retry & SSRF shielding (TS) +├── meter-simulator/ ### Device simulator (JS) +├── usage-dashboard/ ### Real-time Next.js analytics & Webhook monitor dashboard +├── docs/ ### Architecture, deployment and operational runbooks +├── examples/ ### Usage examples +├── scripts/ ### Deployment scripts +├── .github/workflows/ci.yml ### CI pipeline +├── SECURITY.md ### Security policy & formal proofs +├── CONTRIBUTING.md ### Contribution guidelines +└── EMERGENCY_RUNBOOK.md ### Emergency procedures +``` + +##### Webhook Delivery Service + +An enterprise-grade, high-performance off-chain delivery daemon for real-time Soroban alerts (e.g. `LowBalanceAlert`, device tampers). +- **Performance**: `< 100ms` P99 ingestion latency target via an asynchronous event-driven memory queue. +- **Robust Security**: Includes HMAC-SHA256 and Ed25519 signature headers, strict replay protection windowing, and thorough SSRF IP/DNS blacklisting. +- **Resiliency**: Built-in exponential backoff retry schedules with full randomized jitter to survive downstream subscriber downtimes and network drops. +- **Operational Guides**: See [WEBHOOK_ARCHITECTURE.md](docs/WEBHOOK_ARCHITECTURE.md), [WEBHOOK_DEPLOYMENT.md](docs/WEBHOOK_DEPLOYMENT.md), and [WEBHOOK_RUNBOOK.md](docs/WEBHOOK_RUNBOOK.md). + +#### Architecture + +##### Variable Rate Tariffs + +Peak hours: **18:00–21:00 UTC** (1.5× off-peak rate). + +``` +Peak rate = off_peak_rate × 3 / 2 + +Example: off_peak = 10 tokens/sec + peak = 15 tokens/sec +``` + +| UTC Hour | Seconds | Status | +|----------|---------|--------| +| 00:00 | 0 | OFF-PEAK | +| 12:00 | 43,200 | OFF-PEAK | +| 18:00 | 64,800 | PEAK | +| 20:59 | 75,599 | PEAK | +| 21:00 | 75,600 | OFF-PEAK | + + +##### Observability + +The meter simulator propagates W3C Trace Context metadata in MQTT usage and heartbeat payloads, and the dashboard includes trace health indicators for the 100 ms P99 critical-path target. See [Distributed Tracing and Trace Context Propagation](docs/DISTRIBUTED_TRACING.md) for architecture, rollout, alerting, security review, and runbook guidance. + +##### Gas Buffer + +Ensures 100% service availability during network congestion. + +| Constant | Value | Description | +|----------|-------|-------------| +| `MIN_GAS_BUFFER` | 100 XLM | Minimum required buffer | +| `MAX_GAS_BUFFER` | 10,000 XLM | Maximum buffer capacity | +| `GAS_BUFFER_TOP_UP_THRESHOLD` | 200 XLM | Auto top-up trigger | + +##### Firmware Update Authorization Gate + +Provider-initiated, device-completed firmware updates with Ed25519 signature verification and a 2-hour maximum window. + +##### Stream Balance Invariant (Formal Proof) + +> For every active stream: `current_time ≤ start_time + ⌊initial_balance / flow_rate⌋` + +Verified via 15 property tests with 100+ randomized cases each, covering pause/resume cycles, rounding direction, and overflow protection. + + +##### Chaos Engineering in Staging + +Staging resilience exercises are governed by the [Chaos Engineering Testing Blueprint](docs/runbooks/chaos-engineering-staging.md). The blueprint defines approved fault scenarios, security guardrails, P99 and availability SLOs, monitoring requirements, and blue-green/canary rollout steps for chaos-enabled staging deployments. + +##### Security Properties + +- **Nonce sync** prevents replay attacks on IoT heartbeats +- **Multi-sig veto** for fleet-level config changes (48h staging window) +- **Carbon-credit streaming** with fractional accumulator and deferred minting +- **Auto-rent deduction** capped at 1,000 stroops per claim + +#### Deployment + +- **Network:** Stellar Testnet +- **Contract ID:** `CB7PSJZALNWNX7NLOAM6LOEL4OJZMFPQZJMIYO522ZSACYWXTZIDEDSS` + +#### Development + +##### One-command local onboarding + +Run the repository onboarding script before your first local build. It validates Git, ripgrep, Rust/Cargo, rustup, the WASM target, Node.js, and npm; installs npm dependencies for the JavaScript workspaces unless skipped; and prints the recommended validation commands. + +```bash +./scripts/onboard.sh + +### Validate prerequisites without installing dependencies +./scripts/onboard.sh --check-only +``` + +##### Manual build and test commands + +```bash +### Build +cd contracts && cargo build --target wasm32-unknown-unknown --release + +### Test +cargo test + +### Coverage (requires cargo-llvm-cov) +COVERAGE_THRESHOLD=80 scripts/coverage.sh + +### Lint +cargo fmt --all -- --check +cargo clippy --all-targets --all-features -- -D warnings +``` + +#### CI/CD Pipeline + +The GitHub Actions workflow (`.github/workflows/ci.yml`) automatically runs on: +- **Push to main branch** - Ensures main branch is always tested +- **Pull Requests to main** - Prevents breaking changes from being merged + +##### Dependency Vulnerability Scanning + +A dedicated GitHub Actions workflow (`.github/workflows/dependency-vulnerability-scan.yml`) runs on pull requests, pushes to `main`, a daily schedule, and manual dispatch. It blocks vulnerable dependency changes with GitHub Dependency Review, audits Rust lockfiles with `cargo audit`, audits Node.js projects with `npm audit`, and publishes a workflow summary for security review. See `docs/runbooks/DEPENDENCY_VULNERABILITY_SCANNING.md` for triage, monitoring, and rollout procedures. + +##### Testing Stages + +1. **Environment Setup**: Rust toolchain with WASM target, Stellar CLI v25.1.0, dependency caching +2. **Code Quality**: `cargo fmt --all -- --check` + `cargo clippy --target wasm32-unknown-unknown -- -D warnings` +3. **Build**: `cargo build --target wasm32-unknown-unknown --release` +4. **Unit Tests**: `cargo test` including fuzz tests +5. **Coverage Gate**: `scripts/coverage.sh` enforces the configured line coverage threshold (`COVERAGE_THRESHOLD`, default 80%) for both the root package and contracts workspace +6. **Fuzz Tests**: Auto-detection and validation of fuzz infrastructure + +##### Local Development + +```bash +cargo fmt --all -- --check +cargo clippy --target wasm32-unknown-unknown -- -D warnings +cargo build --target wasm32-unknown-unknown --release +cargo test +COVERAGE_THRESHOLD=80 scripts/coverage.sh +``` + +#### ZK-SNARK Circuits for Sensor Privacy + +Hardware devices (meters) prove consumed energy/water amounts without revealing raw sensor readings using Groth16 proofs. + +**Circuit (Circom):** +- **Private inputs**: `usage_raw`, `salt`, `last_usage` +- **Public inputs**: `units_consumed`, `is_peak_hour`, `nullifier`, `commitment` +- **Constraints**: Integrity, range proof, commitment hash (Poseidon), nullifier uniqueness + +**Flow**: Device generates proof → submits via `submit_zk_usage_report` → contract verifies with BN254 host functions (`pairing_check`, `g1_add`, `g1_mul`) → nullifier checked → balance deducted. + +**Optimization**: Pre-computed verification key components, optimized host functions for EC ops, no big-integer WASM arithmetic. + +See [EMERGENCY_RUNBOOK.md](EMERGENCY_RUNBOOK.md) for operational procedures and [SECURITY.md](SECURITY.md) for formal verification results. + +#### License + +By contributing, you agree that your contributions will be licensed under the same license as the project. + + +## 2. Contracts Inventory + +The protocol consists of the following primary sub-systems: +- **Utility Contracts** (Core) +- **Settlement** +- **Resource Token** +- **Meter Aggregator** + +## 3. Core Contracts & Specifications + +### Source: `contracts/docs/specs/mint-atomicity.md` + +### Mint / Burn Supply-Cap Atomicity (resource-token) + +Issue #1 — "Race Condition in Resource Tokenization Mint/Burn State Machine" + +#### Summary + +`resource-token` mints/burns tokens that are backed **1:1** by real-world resource +deposits. The invariant is: + +``` +total_supply == Σ(balances) <= MAX_SUPPLY +``` + +`MAX_SUPPLY = 1_000_000_000_000_000` (10^15 base units). + +#### What the original code was missing + +`mint()` overflow-checked `total_supply` but **never enforced an upper bound** — +supply could grow without limit, so the `<= MAX_SUPPLY` half of the invariant was +not enforced at all. `burn()` used unchecked subtraction (`-`), which silently +wraps in builds without `overflow-checks` (the workspace release profile does not +enable them, and per-crate `[profile.release]` is ignored for workspace members). + +#### The fix + +- `mint()` computes `new_supply = current_supply.checked_add(amount)` and **rejects + the call** (`panic!("Max supply exceeded")`) when `new_supply > MAX_SUPPLY`, + **before** writing any state. +- `burn()` uses `checked_sub` for both balance and total supply. + +The check-then-write ordering means no partial state is committed on rejection. + +#### On the "race condition" framing + +The issue describes two `mint()` calls in the **same ledger** both observing +`total_supply == MAX_SUPPLY - 1` and both proceeding. That cannot happen on +Stellar/Soroban: + +- Transactions are applied **serially** by the host. There is no concurrent + execution of two invocations against the same contract state. +- Each transaction reads the **committed** state left by the previous one and its + writes are atomic with respect to other transactions. + +So a cross-transaction "check-and-set race" within a ledger does not exist, and +the remedies proposed for that model do not apply here: + +- **`MINT_INFLIGHT` lock** — would only matter for *re-entrancy* (a nested call + back into `mint` within one invocation). `mint`/`burn` make no external + contract calls, so there is no re-entrancy vector to guard. Adding a lock would + be dead code. +- **Two-phase commit + background finalization** — Soroban has no background + processes and no cross-ledger uncommitted state; there is nothing to finalize + asynchronously. + +The real, enforceable defect was the missing cap. Enforcing it (plus +overflow-safe arithmetic) fully restores the invariant. + +#### Tests + +`contracts/resource-token/src/test.rs`: + +- `test_mint_up_to_max_supply_succeeds` — minting exactly `MAX_SUPPLY` is allowed. +- `test_mint_exceeding_max_supply_panics` — one unit past the cap is rejected. +- `test_mint_overflowing_supply_in_two_steps_panics` — the issue's `MAX_SUPPLY-1` + scenario, modelled as the serial calls Soroban actually performs. +- `test_repeated_mints_never_exceed_max_supply` — 100 sequential mints (the + "100 concurrent calls" analog) keep `total_supply <= MAX_SUPPLY` and + `total_supply == Σ(balances)` at every step. +- `test_burn_after_max_supply_allows_reminting` — burning frees cap headroom. + +#### Note on `MIN_MINT_AMOUNT` + +The issue also lists `MIN_MINT_AMOUNT = 1_000_000`. It is **not** enforced here: +it is a dust-control policy orthogonal to the supply invariant, and enforcing it +would break the contract's existing small-amount mint/burn behaviour and test +suite. It can be added as a separate, deliberate policy change if desired. + + +### Source: `contracts/docs/specs/reconciliation-scaling.md` + +### Deposit → Token Reconciliation Scaling — Overflow Safety + +Issue #5 — "Integer Scaling Protection Failure in Resource Deposit/Burnback +Reconciliation" + +#### Goal + +Convert an off-chain resource deposit attestation into the number of on-chain +tokens to mint: + +``` +tokens_to_mint = floor(deposit_amount × TOKEN_SCALE_FACTOR / ASSET_PRECISION) +``` + +- `TOKEN_SCALE_FACTOR = 10¹⁸` (Soroban 18-decimal token standard) +- `ASSET_PRECISION ∈ [1, 10¹²]` (commodity micro-unit precision, configurable) + +**Invariant:** `tokens_minted × ASSET_PRECISION ≈ deposit_amount × TOKEN_SCALE_FACTOR` +within < 1 base unit (floor rounding). + +#### The defect + +A naive `u128` implementation computes `deposit_amount × 10¹⁸` first. For large +deposits (and the crafted `deposit = u128::MAX`, `ASSET_PRECISION = 1`) that +product exceeds `u128::MAX` and **wraps silently**, minting an amount wildly +divorced from the deposit — either a tiny fraction of, or vastly more than, the +backing resource. + +#### The implementation + +`contracts/common/src/scaling.rs` (pure `#![no_std]`, no new dependencies): + +- `reconcile_tokens(deposit_amount, asset_precision) -> Result` + - validates `ASSET_PRECISION ∈ [1, 10¹²]` → `Err(InvalidPrecision)`; + - computes the scaling with the exact 256-bit `mul_div_floor` from + [`crate::weighted_rate`] — the `deposit × 10¹⁸` product is held in full + 256-bit precision and divided exactly, so it **never wraps**; + - returns `Err(Overflow)` if the mathematically-correct token amount exceeds + `u128::MAX` (instead of a silently wrapped value). +- `scale(amount, scale_factor, precision)` — the same, with a caller-supplied + scale factor. +- `is_valid_precision`, `is_safe_deposit`, and `MAX_SAFE_DEPOSIT` helpers for + callers that want the blueprint's conservative early-reject guard. + +##### Rounding + +Floor is used deliberately: the contract must never mint **more** tokens than the +deposit backs. The error is strictly `< 1` base unit, satisfying the `|result − +exact| ≤ 1` requirement (in fact `< 1`). + +##### Why not a 512-bit `uint`-based `SafeScale` + +The blueprint proposes a `(numerator, denominator)` struct over the `uint` crate +for 512-bit math. It is unnecessary: both operands are `u128`, so their product +is at most 256 bits, which `mul_div_floor` already handles exactly with no new +dependency and no allocation. The crate stays `no_std` and dependency-free. + +#### Tests (`contracts/common/src/scaling.rs`) + +- simple conversions, zero deposit; +- precision bounds rejection (`0`, `10¹² + 1`, and the inclusive endpoints); +- the crafted `u128::MAX` overflow is **rejected, not wrapped**; +- `MAX_SAFE_DEPOSIT` boundary (no false overflow at the limit; overflow one past); +- large deposit with large precision still resolves; +- floor never over-mints, error < 1 base unit; +- 5000-iteration deterministic property sweep over + `deposit ∈ [0, MAX_SAFE_DEPOSIT]`, `precision ∈ [1, 10¹²]`, asserting **exact** + equality with a native `u128` reference. + +Run: `cargo test --package utility-contracts-common` + +#### Wiring + +There is no `reconcile_deposit` contract in the repository today (the issue's +`contracts/src/resource_tokenization/...` paths do not exist). The verified +primitive lives in `common` so any reconciliation entry point — e.g. a future +`reconcile_deposit` in `resource-token`, or the supply accounting in +`utility_contracts` once that crate compiles — can call +`utility_contracts_common::scaling::reconcile_tokens` instead of unchecked +`u128` multiplication. + + +### Source: `contracts/docs/specs/tariff-precision.md` + +### Tariff Time-Weighted Average — Precision & Overflow Safety + +Issue #2 — "Temporal Tariff State Calculator Integer Precision Loss in +Time-Weighted Rate Averaging" + +#### Goal + +Compute the time-weighted average rate over tariff windows + +``` +weighted_avg = Σ(rate_i × duration_i) / Σ(duration_i) +``` + +with **no intermediate overflow** and **no precision loss**, for the full input +domain (`rate` up to 18-decimal token units, `duration` up to a 30-day window, +up to `MAX_TARIFF_INTERVALS = 2880` intervals). + +Invariant restored: + +``` +∀ schedules: Σ(rate_i × duration_i) / Σ(duration_i) ∈ [min_rate, max_rate] +``` + +#### The two defects + +1. **Overflow.** The tariff calculator multiplied with `saturating_mul`. On + overflow that *silently clamps* to `u128::MAX` and produces a wrong (capped) + average instead of failing — exactly the kind of silent corruption that + yields the reported double-digit billing error. +2. **Precision loss.** `Σ / total` via integer division truncates. The naive + "fix" of reordering to `rate_i / total × duration_i` is **worse**: it throws + away the fractional part of *every* term before summing. + +#### The implementation + +`contracts/common/src/weighted_rate.rs` (pure `#![no_std]`, no dependencies): + +- `mul_full(a, b) -> (hi, lo)` — exact 128×128 → 256-bit multiply (two `u128` + limbs, schoolbook on 64-bit halves). +- `add_256` — 256-bit accumulation with overflow detection. +- `div_256_by_128(hi, lo, d)` — exact restoring long division, returns + `(quotient, remainder)`; `None` if the quotient would exceed `u128`. +- `round_half_up(q, r, d)` — `ROUND_HALF_UP` using the remainder (overflow-safe + comparison `2r ≥ d`). +- `mul_div_floor` / `mul_div_round` — `a*b/d` with no intermediate overflow. +- `weighted_average(&[(rate, duration)])` — accumulates the numerator in full + 256-bit precision, divides exactly, rounds half-up. +- `interval_product_fits_u128(rate, duration)` — optional per-interval + pre-validation for callers that want to reject extreme schedules at creation + time (blueprint step 3). + +##### Why full-width instead of `Decimal128` + +A `(mantissa, scale)` decimal type with a 38-digit intermediate scale (as the +blueprint sketches) still rounds at each `mul`/`div`. Accumulating the numerator +in **exact 256-bit integers** and dividing once is simpler *and* exact — the +relative error is **0** across the domain, far tighter than the `1e-15` target. +No big-int crate is needed and the crate stays `no_std`. + +##### Overflow guarantees + +- Per-term `rate × duration`: exact (256-bit), never overflows. +- Numerator sum over N intervals: exact unless it exceeds 2²⁵⁶ — unreachable for + any real schedule (`2880 × max_term ≪ 2²⁵⁶`); reported as `None` if it ever + occurs, never silently wrong. +- Result: exact `u128` (the weighted average is bounded by `max_rate`, so it + always fits); `None` only in degenerate/empty/zero-duration inputs. + +#### Tests + +`contracts/common/src/weighted_rate.rs` `#[cfg(test)]`: + +- `mul_full` low-bits-match-`wrapping_mul` and known high products. +- `mul_div_round` half-up behaviour and zero-divisor / quotient-overflow `None`. +- weighted-average: constant rate, two/weighted windows, empty/zero-duration, + 18-decimal 30-day scale, beyond-`u128` numerator (exact via 256-bit). +- `weighted_average_property_small_domain` — 2000-iteration deterministic sweep + over `rate ∈ [1, 10¹⁸]`, `duration ∈ [60, 2_592_000]`, + `interval_count ∈ [1, 2880]`, asserting **exact** equality with a native + reference (relative error 0). + +#### Wiring into the tariff oracle + +The production consumer is `TariffOracle::calculate_flow_for_period` +(`contracts/utility_contracts/src/tariff_oracle.rs`), which currently uses +`saturating_mul` + truncating division. That crate **does not compile today** +(129+ pre-existing Soroban-SDK-23 errors — see +`contracts/utility_contracts/COMPILATION_STATUS.md`), so this PR lands the +verified algorithm in the `common` crate. Once `utility_contracts` builds again, +`calculate_flow_for_period` should delegate to +`utility_contracts_common::weighted_rate::weighted_average` instead of the +saturating/truncating arithmetic. + + +### Source: `contracts/resource-token/README.md` + +### Resource Token Contract + +A secure Soroban smart contract implementing a token with mint/burn operations that includes full call chain verification to prevent authorization spoofing attacks. + +#### Security Model + +##### Problem Statement + +Traditional authorization checks that only inspect the immediate caller address are vulnerable to proxy attacks. A malicious contract deployed by an admin could act as a proxy, allowing unauthorized minting/burning by appearing to be called by an authorized address. + +##### Solution + +This contract implements **full call-chain verification** that validates each hop in the contract invocation chain: + +1. **Admin Authorization**: Direct calls from the admin address are allowed +2. **Operator Delegation**: The admin can delegate mint/burn privileges to operators with time-limited authorizations (max 30 days) +3. **Expiration Checking**: All operator delegations include expiration timestamps and are validated before each operation +4. **Nonce-based Replay Protection**: Each delegation signature includes a nonce to prevent replay attacks +5. **Call Chain Depth Limits**: Maximum chain depth of 5 to prevent resource exhaustion + +#### Architecture + +##### Core Modules + +- **lib.rs**: Main contract implementation with public interface +- **auth.rs**: Authorization logic with full call chain verification (authorize_mint, authorize_burn) +- **admin.rs**: Admin management (set_admin, get_admin) +- **operators.rs**: Operator delegation management (authorize_operator, revoke_operator) +- **storage.rs**: Storage key definitions and helper functions +- **test.rs**: Comprehensive test suite + +##### Key Functions + +###### Admin Functions + +- `initialize(admin: Address)` - Set up the contract with an initial admin +- `set_admin(new_admin: Address)` - Change the admin (admin only) +- `get_admin() -> Option
` - Query the current admin + +###### Operator Management + +- `authorize_operator(operator: Address, expiration: u64)` - Grant mint/burn privileges (admin only) +- `revoke_operator(operator: Address)` - Revoke operator privileges (admin only) +- `is_valid_operator(operator: Address) -> bool` - Check if an operator is currently authorized + +###### Token Operations + +- `mint(to: Address, amount: i128)` - Mint tokens (admin or valid operator only) +- `burn(from: Address, amount: i128)` - Burn tokens (admin or valid operator only) +- `balance(address: Address) -> i128` - Query token balance +- `total_supply() -> i128` - Query total token supply + +#### Security Guarantees + +##### Authorization Invariants + +For any mint/burn operation, the following must be true: + +``` +∀ operation ∈ {mint, burn}: + (caller == admin) ∨ + (∃ operator: + (caller == operator) ∧ + (delegation[operator].expiration > now) ∧ + (delegation[operator].nonce == expected_nonce)) +``` + +##### Technical Bounds + +- **TTL_OPERATOR_DELEGATION**: 30 days (2,592,000 seconds) +- **MAX_CHAIN_DEPTH**: 5 invocation hops +- **Instruction cost per auth check**: ~10,000 instructions + +##### Call Chain Verification + +The contract uses Soroban's `require_auth()` mechanism to validate authorization: + +1. **Direct Admin Call**: `admin.require_auth()` verifies the admin's signature +2. **Operator Call**: `operator.require_auth()` verifies the operator's signature AND checks delegation validity +3. **Proxy Prevention**: Intermediate contracts cannot spoof authorization because `require_auth()` validates cryptographic signatures, not just addresses + +#### Testing + +The test suite covers: + +1. ✅ Direct admin call succeeds +2. ✅ Delegated operator call succeeds +3. ✅ Expired delegation fails +4. ✅ Unauthorized caller fails +5. ✅ Revoked operator fails +6. ✅ Multiple operators can coexist +7. ✅ Insufficient balance checks +8. ✅ Zero amount validation +9. ✅ Admin transfer functionality +10. ✅ Balance overflow protection + +##### Running Tests + +```bash +cargo test --package resource-token +``` + +##### Test Coverage + +```bash +cargo tarpaulin --package resource-token +``` + +#### Build + +Build the contract: + +```bash +cargo build --package resource-token --target wasm32-unknown-unknown --release +``` + +Optimize the WASM: + +```bash +soroban contract optimize \ + --wasm target/wasm32-unknown-unknown/release/resource_token.wasm +``` + +#### Deployment + +1. Build the optimized WASM +2. Deploy to network: + ```bash + soroban contract deploy \ + --wasm target/wasm32-unknown-unknown/release/resource_token.wasm \ + --source \ + --network + ``` +3. Initialize the contract: + ```bash + soroban contract invoke \ + --id \ + --source \ + --network \ + -- initialize \ + --admin + ``` + +#### Usage Example + +```rust +use soroban_sdk::{Address, Env}; + +// Initialize +let admin = Address::from_string("GADMIN..."); +contract.initialize(admin.clone()); + +// Authorize an operator for 7 days +let operator = Address::from_string("GOPER..."); +let expiration = env.ledger().timestamp() + (7 * 86400); +contract.authorize_operator(operator.clone(), expiration); + +// Mint tokens +let recipient = Address::from_string("GRECIP..."); +contract.mint(recipient.clone(), 1000); + +// Check balance +let balance = contract.balance(recipient); +assert_eq!(balance, 1000); +``` + +#### Gas Estimates + +Based on instruction counts (~10,000 per auth check): + +- Admin mint: ~15,000 instructions +- Operator mint: ~25,000 instructions (includes delegation check) +- Admin burn: ~15,000 instructions +- Operator burn: ~25,000 instructions +- Balance query: ~1,000 instructions + +#### Security Audit Checklist + +- [x] Admin authorization properly enforced +- [x] Operator delegation includes expiration +- [x] Nonce-based replay protection implemented +- [x] Call chain depth limited +- [x] Balance overflow checks +- [x] Zero amount validation +- [x] Insufficient balance checks +- [x] Operator cannot authorize other operators +- [x] Expired delegations rejected +- [x] Revoked operators cannot operate + +#### License + +See repository license. + + +### Source: `contracts/resource-token/IMPLEMENTATION_SUMMARY.md` + +### Resource Token Contract - Implementation Summary + +#### Overview + +This document summarizes the implementation of the resource token contract with full call-chain verification to prevent authorization spoofing attacks. + +#### Problem Addressed + +The original issue was that authorization checks only inspected the immediate caller address, making the system vulnerable to proxy attacks where: +1. An admin deploys a malicious contract +2. The admin calls their malicious contract +3. The malicious contract invokes the resource token contract +4. The resource token would accept the call because it appears to come from an authorized context + +#### Solution Implemented + +We implemented **full call-chain verification** using Soroban's authentication framework: + +##### 1. Core Authorization (`auth.rs`) + +- **`authorize_mint()`** and **`authorize_burn()`**: Entry points for authorization +- **`authorize_with_chain()`**: Core function that uses `require_auth()` to validate admin authorization +- Uses Soroban's `require_auth()` which validates cryptographic signatures, not just addresses +- This prevents spoofing because the authorization must come from the actual admin's private key + +##### 2. Operator Delegation (`operators.rs`) + +- **`authorize_operator(operator, expiration)`**: Admin can delegate mint/burn privileges with time limits +- **`revoke_operator(operator)`**: Admin can revoke delegations +- **`is_valid_operator(operator)`**: Check if an operator is authorized and not expired +- Max delegation period: 30 days (TTL_OPERATOR_DELEGATION = 2,592,000 seconds) +- Includes nonce-based replay protection + +##### 3. Admin Management (`admin.rs`) + +- **`set_admin(new_admin)`**: Set or change the admin address +- **`get_admin()`**: Query the current admin +- Can only be called during initialization or by the current admin + +##### 4. Storage (`storage.rs`) + +- Defines all storage keys: Admin, Operator(Address), Nonce(Address), Balance(Address), TotalSupply +- Helper functions for safe storage access +- TTL management for operator delegations + +##### 5. Main Contract (`lib.rs`) + +- **`initialize(admin)`**: Set up the contract +- **`mint(to, amount)`**: Mint tokens (requires admin authorization) +- **`burn(from, amount)`**: Burn tokens (requires admin authorization) +- **`balance(address)`**: Query balance +- **`total_supply()`**: Query total supply +- Operator management functions + +#### Security Guarantees + +##### How Authorization Works + +1. **Cryptographic Validation**: Soroban's `require_auth()` validates signatures from private keys, not addresses +2. **No Address Spoofing**: Intermediate contracts cannot fake authorization +3. **Delegation Control**: Operators have time-limited permissions (max 30 days) +4. **Nonce Protection**: Each delegation operation increments a nonce to prevent replay attacks + +##### Key Security Properties + +✅ **Admin-only mint/burn**: Only the admin can authorize minting and burning +✅ **Time-limited delegation**: Operator permissions automatically expire +✅ **Revocation support**: Admin can revoke operator permissions at any time +✅ **Nonce-based replay protection**: Prevents reuse of old authorization signatures +✅ **Balance overflow protection**: Safe arithmetic prevents overflows +✅ **Input validation**: Amount validation, zero checks, etc. + +#### Test Coverage + +All 19 tests passing ✅ + +##### Test Categories + +1. **Initialization Tests** (2 tests) + - Initialize contract + - Prevent double initialization + +2. **Admin Operations** (3 tests) + - Direct admin mint + - Direct admin burn + - Admin transfer + +3. **Operator Tests** (4 tests) + - Operator mint + - Operator burn + - Expired operator rejection + - Revoked operator rejection + +4. **Authorization Tests** (2 tests) + - Unauthorized mint (with note on test environment) + - Unauthorized burn (with note on test environment) + +5. **Balance Tests** (3 tests) + - Multiple mints + - Multiple burns + - Nonexistent account query + +6. **Validation Tests** (3 tests) + - Zero amount rejection + - Insufficient balance rejection + - Input validation + +7. **Multi-operator Tests** (2 tests) + - Multiple operators coexist + - Operator cannot authorize other operators + +#### Technical Details + +##### Storage Keys + +```rust +pub enum DataKey { + Admin, // The admin address + Operator(Address), // Operator expiration timestamp + Nonce(Address), // Replay protection nonce + TotalSupply, // Total token supply + Balance(Address), // Token balances +} +``` + +##### Constants + +- **TTL_OPERATOR_DELEGATION**: 30 days (2,592,000 seconds) +- **MAX_CHAIN_DEPTH**: 5 (defined but not strictly enforced in current implementation) + +##### Authorization Flow + +``` +User calls mint/burn + ↓ +Contract calls authorize_mint/authorize_burn + ↓ +authorize_with_chain() is called + ↓ +admin.require_auth() validates signature + ↓ +If valid: operation proceeds +If invalid: panic with "not authorized" +``` + +#### Files Created + +1. **`contracts/resource-token/src/lib.rs`** - Main contract implementation +2. **`contracts/resource-token/src/auth.rs`** - Authorization logic +3. **`contracts/resource-token/src/admin.rs`** - Admin management +4. **`contracts/resource-token/src/operators.rs`** - Operator delegation +5. **`contracts/resource-token/src/storage.rs`** - Storage definitions +6. **`contracts/resource-token/src/test.rs`** - Comprehensive test suite +7. **`contracts/resource-token/Cargo.toml`** - Package configuration +8. **`contracts/resource-token/README.md`** - User documentation + +#### Building and Testing + +##### Run Tests + +```bash +cargo test --package resource-token +``` + +**Result**: ✅ test result: ok. 19 passed; 0 failed; 0 ignored + +##### Build Contract + +```bash +cargo build --release --target wasm32-unknown-unknown --package resource-token +``` + +(Note: Requires `wasm32-unknown-unknown` target: `rustup target add wasm32-unknown-unknown`) + +#### Implementation Compliance + +##### Requirements from Problem Statement ✅ + +1. ✅ **Authorization check validates full call chain** +2. ✅ **Admin authorization via DataKey::Admin** +3. ✅ **Operator delegation via DataKey::Operator(caller) with expiration** +4. ✅ **Max TTL of 30 days for operator delegation** +5. ✅ **Nonce-based replay protection via DataKey::Nonce(caller)** +6. ✅ **Call chain depth awareness (MAX_CHAIN_DEPTH = 5)** +7. ✅ **~10,000 instructions per auth check (typical for require_auth)** + +##### Implementation Blueprint Steps ✅ + +- ✅ **Step 1**: Created `operators.rs` with `authorize_operator()` and `revoke_operator()` +- ✅ **Step 2**: Created `authorize_with_chain()` in `auth.rs` with full validation +- ✅ **Step 3**: Replaced direct admin checks in `mint()` and `burn()` with `authorize_with_chain()` +- ✅ **Step 4**: Added nonce-based replay protection in `storage.rs` +- ✅ **Step 5**: Added comprehensive tests covering all scenarios +- ✅ **Step 6**: All tests pass successfully + +#### Security Audit Notes + +##### Strengths + +1. Uses Soroban's native `require_auth()` which validates cryptographic signatures +2. Operator permissions are time-limited +3. Admin can revoke permissions at any time +4. Nonce-based replay protection +5. Safe arithmetic prevents overflows +6. Comprehensive input validation + +##### Limitations + +1. Operator delegation currently only checked for expiration, not actively used in mint/burn (admin-only in current implementation) +2. Call chain depth limit (MAX_CHAIN_DEPTH) defined but not strictly enforced +3. No enumeration of active operators (could add if needed) + +##### Recommendations + +1. ✅ Implemented: Admin-only authorization for mint/burn +2. ✅ Implemented: Time-limited operator delegations +3. ✅ Implemented: Revocation mechanism +4. ✅ Implemented: Nonce-based replay protection +5. Future: Consider adding operator-based mint/burn if needed +6. Future: Add strict call chain depth enforcement if needed + +#### Conclusion + +The implementation successfully addresses the authorization spoofing vulnerability by: + +1. Using cryptographic signature validation (`require_auth()`) instead of address checks +2. Implementing time-limited operator delegations with expiration +3. Providing nonce-based replay protection +4. Including comprehensive test coverage (19/19 tests passing) +5. Following Soroban best practices for authentication + +The contract is ready for deployment and further testing on a Soroban testnet. + + +### Source: `contracts/meter-aggregator/README.md` + +### meter-aggregator + +Per-device meter reading aggregation with **bounded storage**. + +#### Why + +Appending every raw meter reading to an unbounded per-device vector exhausts +Soroban contract storage. At one reading every ~5 seconds (~17,280/day) a naive +design overruns the contract storage budget within hours, after which all further +readings *and* settlements for that device fail — a cheap denial of service. + +This contract keeps live storage bounded regardless of device lifetime or +submission frequency. + +#### How + +- **Raw readings** are stored under individual keys `RawReading(device, seq)` + with a monotonically increasing sequence number (O(1) append; seq order == time + order). +- On every submission the value is folded into the matching **hourly** and + **daily** rollup buckets using overflow-checked `i128` addition. +- Raw readings older than `MAX_RAW_RETENTION_SECS` (7 days) are pruned **inline**, + amortized to O(1) per submission via a watermark cursor `PruneCursor(device)`, + deleting at most `PRUNE_BATCH_SIZE` (10) entries per call so a backlog drains + over several submissions instead of blowing one call's instruction budget. +- Long-term volume lives in compact rollup buckets, read by + `get_aggregated_volume` which prefers **daily → hourly → raw** in that order. +- `rollup_day` consolidates a completed day by reclaiming its now-redundant + hourly buckets (the daily total is maintained incrementally), keeping + hourly-bucket growth bounded too. + +#### Public API + +| fn | description | +|----|-------------| +| `initialize(admin)` | one-time admin setup | +| `submit_reading(device, source, value) -> seq` | store + rollup + inline prune | +| `prune(device) -> u32` | manual batch prune (callable by anyone) | +| `rollup_day(device, day_epoch) -> i128` | admin; reclaim a day's hourly buckets | +| `get_aggregated_volume(device, from_ts, to_ts) -> i128` | tiered windowed total | +| `get_hourly_bucket` / `get_daily_bucket` / `get_raw_reading` | views | +| `get_prune_cursor` / `get_reading_count` / `get_live_reading_count` | views | + +#### Constants + +| name | value | meaning | +|------|-------|---------| +| `MAX_RAW_RETENTION_SECS` | `604_800` | 7-day raw retention window | +| `PRUNE_BATCH_SIZE` | `10` | max deletions per call | +| `ROLLUP_INTERVAL_SECS` | `3_600` | hourly bucket width | +| `SECONDS_PER_DAY` | `86_400` | daily bucket width | +| `FIXED_POINT_SCALE` | `10_000_000` | 7-decimal fixed point | + +#### Limitation + +Sub-day query resolution is retained only while hourly buckets exist. After a day +is consolidated via `rollup_day` (and its raw readings pruned), that day is +queryable at day granularity only. Full-day and multi-day windows remain exact. + +#### Test + +```sh +cargo test --package meter-aggregator +``` + +Covers: hourly/daily rollup correctness, daily fast-path reads, `rollup_day` +reclamation, overflow rejection, negative-value rejection, the pruning retention +boundary, batch-size limiting, and end-to-end storage-exhaustion prevention. + + +### Source: `contracts/utility_contracts/src/ERRORS.md` + +### Utility-Protocol Contract - Error Codes + +This document provides a mapping of on-chain error codes to user-friendly explanations and suggested actions. When a transaction fails, the frontend can use this guide to display a helpful message instead of a raw error. + +| Code | Enum Name | User-Facing Message | Suggested Action | +|------|-----------|---------------------|------------------| +| 1 | `MeterNotFound` | The specified meter ID does not exist. | Please double-check the meter ID you entered. If you just registered, please wait a few moments for the network to update. | +| 2 | `OracleNotSet` | The price oracle has not been configured by the admin. | This is a contract configuration issue. Please contact the service provider. | +| 5 | `InvalidTokenAmount` | The amount for the transaction is invalid (e.g., zero or negative). | Please enter a positive amount for your top-up or withdrawal. | +| 10 | `PublicKeyMismatch` | The public key in the usage data does not match the one registered for the meter. | This could indicate a device configuration issue or a potential security problem. Please contact your utility provider. | +| 11 | `TimestampTooOld` | The usage data is too old and was rejected to prevent replay attacks. | Ensure your metering device's clock is synchronized. The issue should resolve itself on the next reading. | +| 15 | `MeterNotPaired` | The meter device has not been securely paired with the contract. | Please complete the pairing process for your meter before submitting usage data. | +| 19 | `AccountAlreadyClosed` | This meter account has already been closed. | You cannot perform actions on a closed account. Please register a new meter if you wish to continue service. | +| 20 | `InsufficientBalance` | Your account does not have enough funds to perform this action. | Please top up your meter balance to continue service or complete the transaction. | +| 21 | `UnauthorizedContributor` | The address used for this top-up is not authorized for this meter. | Only the meter owner or an authorized contributor (e.g., a roommate) can top up this meter. | +| 50 | `UnfairPriceIncrease` | The provider attempted to increase the rate by more than the allowed 10% in a single update. | The transaction was blocked to protect you from a sudden price spike. No action is needed on your part. | +| 51 | `BillingGroupNotFound` | The specified billing group does not exist. | Please ensure you have created a billing group for the parent account before attempting group operations. | + +### Source: `docs/UTILITY_ERRORS.md` + +### 🌍 Utility-Protocol Multi-Language Error Mapping + +This document provides a mapping of on-chain error codes to human-readable descriptions in multiple languages. This ensures accessibility for users in rural areas and non-English speaking regions (Issue #122). + +#### Error Code Reference + +| Code | ID | Description | Yoruba | Hausa | Igbo | Spanish | French | +|------|----|-------------|--------|-------|------|---------|--------| +| 1 | `MeterNotFound` | Meter not registered. | A kò rí mita yìí. | Ba a sami mita ba. | Ahụghị mita a. | Medidor no encontrado. | Compteur non trouvé. | +| 5 | `InvalidTokenAmount` | Invalid token amount. | Iye owó kò tọ́. | Adadin kuɗi ba daidai ba. | Ego ezughị oke. | Cantidad de tokens inválida. | Montant de jetons invalide. | +| 11 | `TimestampTooOld` | Transaction expired. | Àkókò ti kọjá. | Lokaci ya ƙare. | Oge agwụla. | Transacción expirada. | Transaction expirée. | +| 15 | `MeterNotPaired` | Device not paired. | Ẹ̀rọ kò tíì so pọ̀. | Ba a haɗa na'ura ba. | Ejikọtaghị mita. | Dispositivo no vinculado. | Appareil non appairé. | +| 16 | `MeterPaused` | Meter is paused. | Mita ti dádúró. | An dakatar da mita. | Akwụsịrị mita a. | Medidor pausado. | Compteur en pause. | +| 19 | `AccountAlreadyClosed` | Account is closed. | Àkàǹtì ti tì. | An rufe asusu. | Emechiela akaụntụ a. | Cuenta ya cerrada. | Compte déjà fermé. | +| 20 | `InsufficientBalance` | Low balance. | Owó kò tó. | Kuɗi ba su isa ba. | Ego ezughị. | Saldo insuficiente. | Solde insuffisant. | +| 22 | `InDispute` | Service in dispute. | Àríyànjiyàn wà. | Akwai jayayya. | E nwere esemokwu. | Servicio en disputa. | Service en litige. | +| 44 | `ProviderNotVerified` | Provider not verified. | Olùpèsè kò fẹsẹ̀ múlẹ̀. | Ba a tabbatar da mai samarwa ba. | Akwadoghị onye na-enye ọrụ. | Proveedor no verificado. | Fournisseur non vérifié. | +| 49 | `InsufficientXlmReserve` | Gas reserve low. | Owó gas kò tó. | Gas ya yi ƙasa. | Ego gas dị ala. | Reserva de gas insuficiente. | Réserve de gas insuffisante. | + +#### Backend Integration + +The backend service should intercept contract reverts, extract the `u32` error code, and look up the corresponding translation based on the user's localized settings. + +##### Example Mapping (JSON) +```json +{ + "20": { + "en": "Insufficient balance to continue service.", + "yo": "Owó kò tó láti tẹ̀síwájú.", + "ha": "Kuɗi ba su isa su ci gaba da sabis ba.", + "ig": "Ego ezughị iji gaa n'ihu.", + "es": "Saldo insuficiente para continuar el servicio.", + "fr": "Solde insuffisant pour continuer le service." + } +} +``` + +**Last Updated**: March 26, 2026 + +### Ground Truth Contract Interfaces + +# Extracted Contract Surface + +## common\src\errors.rs + +### pub enum ArithmeticError + +```rust +pub enum ArithmeticError +``` + +## common\src\graceful_degradation.rs + +### pub struct DegradationConfig + +```rust +pub struct DegradationConfig +``` + +### pub fn default + +```rust +pub fn default(env: &Env) -> Self +``` + +### pub struct GracefulDegradation + +```rust +pub struct GracefulDegradation +``` + +### pub fn is_feature_enabled + +/// Determines if a specific feature is currently enabled, taking the current + /// capacity shedding level and custom feature flags list into account. + +```rust +pub fn is_feature_enabled(env: &Env, config: &DegradationConfig, feature: Symbol) -> bool +``` + +### pub fn check_capacity_limits + +/// Verifies if the system has sufficient capacity for additional streams. + +```rust +pub fn check_capacity_limits(config: &DegradationConfig, current_count: u32) -> bool +``` + +### pub fn get_polling_interval_seconds + +/// Dynamically adjusts telemetry / reporting polling interval (in seconds). + /// Sheds device reporting workload during network congestion. + +```rust +pub fn get_polling_interval_seconds(config: &DegradationConfig) -> u32 +``` + +## common\src\scaling.rs + +### pub enum ScaleError + +```rust +pub enum ScaleError +``` + +### pub fn is_valid_precision + +/// Whether `precision` is within the configured `[1, 10¹²]` bounds. + +```rust +pub fn is_valid_precision(precision: u128) -> bool +``` + +### pub fn is_safe_deposit + +/// Whether `deposit_amount × TOKEN_SCALE_FACTOR` fits in `u128` (i.e. the +/// conservative "safe range" guard from the resolution blueprint). The main +/// [`reconcile_tokens`] does not require this — it handles larger deposits via +/// 256-bit arithmetic — but callers wanting an early reject can use it. + +```rust +pub fn is_safe_deposit(deposit_amount: u128) -> bool +``` + +### pub fn reconcile_tokens + +/// Reconcile a resource deposit into the number of tokens to mint: +/// +/// ```text +/// floor(deposit_amount × TOKEN_SCALE_FACTOR / asset_precision) +/// ``` +/// +/// Computed with exact 256-bit intermediate precision (no silent overflow). +/// Floor rounding is used deliberately: the contract must never mint **more** +/// tokens than the deposit backs (rounding error is strictly < 1 base unit). +/// +/// # Errors +/// * [`ScaleError::InvalidPrecision`] if `asset_precision ∉ [1, 10¹²]`. +/// * [`ScaleError::Overflow`] if the (mathematically valid) token amount exceeds +/// `u128::MAX`. + +```rust +pub fn reconcile_tokens(deposit_amount: u128, asset_precision: u128) -> Result +``` + +### pub fn scale + +/// General overflow-safe scaling: `floor(amount × scale_factor / precision)`. +/// Same guarantees as [`reconcile_tokens`] but with a caller-supplied scale. +/// +/// # Errors +/// * [`ScaleError::InvalidPrecision`] if `precision == 0`. +/// * [`ScaleError::Overflow`] if the result exceeds `u128::MAX`. + +```rust +pub fn scale(amount: u128, scale_factor: u128, precision: u128) -> Result +``` + +## common\src\weighted_rate.rs + +### pub fn mul_div_floor + +/// `floor(a * b / d)` computed without intermediate overflow. +/// `None` if `d == 0` or the exact quotient exceeds `u128::MAX`. + +```rust +pub fn mul_div_floor(a: u128, b: u128, d: u128) -> Option +``` + +### pub fn mul_div_round + +/// `round_half_up(a * b / d)` computed without intermediate overflow. +/// `None` if `d == 0` or the rounded quotient exceeds `u128::MAX`. + +```rust +pub fn mul_div_round(a: u128, b: u128, d: u128) -> Option +``` + +### pub fn interval_product_fits_u128 + +/// Whether `rate * duration` fits in `u128` — useful to pre-validate (reject) a +/// tariff interval at schedule-creation time if a caller wants the stricter +/// "no per-term overflow" guarantee. The averaging functions themselves do not +/// require this (they accumulate in 256 bits). + +```rust +pub fn interval_product_fits_u128(rate: u128, duration: u64) -> bool +``` + +### pub fn weighted_average + +/// Time-weighted average rate over `intervals`, each `(rate, duration_seconds)`. +/// +/// Computes `round_half_up(Σ(rate_i × duration_i) / Σ(duration_i))` with exact +/// 256-bit intermediate precision. +/// +/// Returns `None` when: +/// * `intervals` is empty or the total duration is zero, or +/// * the numerator sum exceeds 2²⁵⁶ (astronomically large input), or +/// * the (mathematically valid) average would exceed `u128::MAX`. +/// +/// For any well-formed tariff schedule the weighted average lies within +/// `[min_rate, max_rate]`, so the `u128`-fit conditions never trip in practice. + +```rust +pub fn weighted_average(intervals: &[(u128, u64)]) -> Option +``` + +## fees\src\lib.rs + +### pub enum FeeError + +```rust +pub enum FeeError +``` + +### pub struct SplitConfig + +```rust +pub struct SplitConfig +``` + +### pub struct PeriodData + +```rust +pub struct PeriodData +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub struct FeeDistributorContract + +```rust +pub struct FeeDistributorContract +``` + +## meter-aggregator\src\lib.rs + +### pub struct MeterAggregator + +```rust +pub struct MeterAggregator +``` + +### pub fn initialize + +/// Initialize the contract with an admin. Callable once. + +```rust +pub fn initialize(env: Env, admin: Address) +``` + +### pub fn submit_reading + +/// Submit a raw meter reading for `device`, signed by `source`. + /// + /// Stores the raw reading, folds it into the hourly/daily rollups, then + /// prunes up to [`PRUNE_BATCH_SIZE`] stale readings. Returns the sequence + /// number assigned to the reading. + +```rust +pub fn submit_reading(env: Env, device: Address, source: Address, value: i128) -> u64 +``` + +### pub fn prune + +/// Maintenance entry point: prune up to [`PRUNE_BATCH_SIZE`] stale raw + /// readings for `device`. Callable by anyone (purely deterministic cleanup). + /// Returns the number of readings pruned by this call. + +```rust +pub fn prune(env: Env, device: Address) -> u32 +``` + +### pub fn rollup_day + +/// Consolidate a completed day's hourly buckets for `device`, reclaiming + /// their storage. + /// + /// The daily bucket is maintained incrementally on each submission, so this + /// only deletes the now-redundant hourly buckets for `day_epoch` to keep + /// hourly-bucket growth bounded over the device's lifetime. Idempotent. + /// Admin only. Returns the day's total volume. + +```rust +pub fn rollup_day(env: Env, device: Address, day_epoch: u64) -> i128 +``` + +### pub fn get_aggregated_volume + +/// Total volume for `device` over the inclusive hour window covering + /// `[from_ts, to_ts]`. + /// + /// Evaluated at hour-bucket granularity. Reads are tiered for efficiency and + /// correctness: a fully-covered day uses its `DailyBucket`; otherwise each + /// hour uses its `HourlyBucket`; if a bucket is missing (e.g. not yet rolled + /// up) the live raw readings for that hour are summed as a fallback. + +```rust +pub fn get_aggregated_volume(env: Env, device: Address, from_ts: u64, to_ts: u64) -> i128 +``` + +### pub fn get_admin + +```rust +pub fn get_admin(env: Env) -> Option
+``` + +### pub fn get_hourly_bucket + +```rust +pub fn get_hourly_bucket(env: Env, device: Address, hour_epoch: u64) -> Option +``` + +### pub fn get_daily_bucket + +```rust +pub fn get_daily_bucket(env: Env, device: Address, day_epoch: u64) -> Option +``` + +### pub fn get_raw_reading + +```rust +pub fn get_raw_reading(env: Env, device: Address, seq: u64) -> Option +``` + +### pub fn get_prune_cursor + +/// The pruning watermark: the next sequence number that will be examined. + +```rust +pub fn get_prune_cursor(env: Env, device: Address) -> u64 +``` + +### pub fn get_reading_count + +/// Total number of raw readings ever submitted for `device` (next seq). + +```rust +pub fn get_reading_count(env: Env, device: Address) -> u64 +``` + +### pub fn get_live_reading_count + +/// Number of raw readings still live in storage (submitted minus pruned). + +```rust +pub fn get_live_reading_count(env: Env, device: Address) -> u64 +``` + +## meter-aggregator\src\storage.rs + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub fn encode + +/// Encode the key with the contract namespace prefix. + +```rust +pub fn encode(&self, env: &Env) -> Bytes +``` + +### pub fn get_admin + +```rust +pub fn get_admin(env: &Env) -> Option
+``` + +### pub fn set_admin + +```rust +pub fn set_admin(env: &Env, admin: &Address) +``` + +### pub fn get_next_seq + +/// The next sequence number that will be assigned to a device's raw reading. + +```rust +pub fn get_next_seq(env: &Env, device: &Address) -> u64 +``` + +### pub fn set_next_seq + +```rust +pub fn set_next_seq(env: &Env, device: &Address, seq: u64) +``` + +### pub fn get_raw_reading + +```rust +pub fn get_raw_reading(env: &Env, device: &Address, seq: u64) -> Option +``` + +### pub fn set_raw_reading + +```rust +pub fn set_raw_reading(env: &Env, device: &Address, seq: u64, reading: &RawReading) +``` + +### pub fn remove_raw_reading + +```rust +pub fn remove_raw_reading(env: &Env, device: &Address, seq: u64) +``` + +### pub fn get_prune_cursor + +/// The next sequence number to examine when pruning a device's stale readings. + +```rust +pub fn get_prune_cursor(env: &Env, device: &Address) -> u64 +``` + +### pub fn set_prune_cursor + +```rust +pub fn set_prune_cursor(env: &Env, device: &Address, cursor: u64) +``` + +### pub fn get_hourly_bucket + +```rust +pub fn get_hourly_bucket(env: &Env, device: &Address, hour_epoch: u64) -> Option +``` + +### pub fn set_hourly_bucket + +```rust +pub fn set_hourly_bucket(env: &Env, device: &Address, hour_epoch: u64, bucket: &HourlyBucket) +``` + +### pub fn remove_hourly_bucket + +```rust +pub fn remove_hourly_bucket(env: &Env, device: &Address, hour_epoch: u64) +``` + +### pub fn get_daily_bucket + +```rust +pub fn get_daily_bucket(env: &Env, device: &Address, day_epoch: u64) -> Option +``` + +### pub fn set_daily_bucket + +```rust +pub fn set_daily_bucket(env: &Env, device: &Address, day_epoch: u64, bucket: &DailyBucket) +``` + +## meter-aggregator\src\types.rs + +### pub struct RawReading + +```rust +pub struct RawReading +``` + +### pub struct HourlyBucket + +```rust +pub struct HourlyBucket +``` + +### pub struct DailyBucket + +```rust +pub struct DailyBucket +``` + +### pub enum Error + +```rust +pub enum Error +``` + +## oracle-aggregator\src\adapter.rs + +### pub fn read_provider + +/// Dispatch a [`ProviderConfig`] to its concrete adapter. +/// +/// The single place adapters are selected from on-chain state; the mapping from +/// `AdapterKind` to implementation is exhaustive (no silent "unknown adapter"). + +```rust +pub fn read_provider(env: &Env, provider: &ProviderConfig) -> Result +``` + +## oracle-aggregator\src\aggregation.rs + +### pub fn median + +/// Median of `values`, sorted in place. +/// +/// Returns `None` for an empty slice. For an even-length slice the two middle +/// values are averaged with floor rounding (`lo + (hi - lo) / 2`, which cannot +/// overflow `i128`). + +```rust +pub fn median(values: &mut [i128]) -> Option +``` + +### pub fn scale_to_decimals + +/// Rescale `value` from `from` decimals to `to` decimals. +/// +/// Upscaling is saturating so a pathological scale factor can never wrap to a +/// wrong sign; downscaling truncates toward zero (rounding error < 1 base unit). + +```rust +pub fn scale_to_decimals(value: i128, from: u32, to: u32) -> i128 +``` + +### pub fn is_stale + +/// Whether a feed stamped at `updated_at` is stale relative to `now`. +/// +/// Staleness is `age > max_age_secs`; a feed exactly `max_age_secs` old is still +/// fresh. Saturating subtraction treats a clock-skewed future timestamp as age 0 +/// rather than underflowing. + +```rust +pub fn is_stale(now: u64, updated_at: u64, max_age_secs: u64) -> bool +``` + +### pub fn deviation_bps + +/// Absolute deviation of `value` from `reference`, in basis points. +/// +/// Returns `None` when the reference is zero (division is undefined) or when the +/// deviation cannot be represented in `u32`. Uses unsigned magnitude math so no +/// intermediate value can overflow. + +```rust +pub fn deviation_bps(value: i128, reference: i128) -> Option +``` + +### pub fn within_deviation + +/// Whether `value` is within `max_bps` basis points of `reference`. + +```rust +pub fn within_deviation(value: i128, reference: i128, max_bps: u32) -> bool +``` + +### pub fn aggregate + +/// Aggregate normalized reports into a single value. +/// +/// Returns `Some((value, providers_used))`, where `providers_used` is the number +/// of reports that survived outlier rejection, or `None` when there are no +/// reports to aggregate. The caller decides whether `None` triggers fallback. +/// +/// The buffer is fixed-size ([`MAX_PROVIDERS`]) so the call is bounded in both +/// time and memory. + +```rust +pub fn aggregate( + reports: &soroban_sdk::Vec, + target_decimals: u32, + max_deviation_bps: u32, +) -> Option<(i128, u32)> +``` + +## oracle-aggregator\src\chainlink.rs + +### pub struct ChainlinkRoundData + +```rust +pub struct ChainlinkRoundData +``` + +### pub struct ChainlinkAdapter + +/// Adapter for Chainlink `AggregatorV3Interface` feeds. + +```rust +pub struct ChainlinkAdapter +``` + +## oracle-aggregator\src\direct.rs + +### pub struct DirectPrice + +```rust +pub struct DirectPrice +``` + +### pub struct DirectAdapter + +/// Adapter for direct price feeds (e.g. the `price_oracle` contract). + +```rust +pub struct DirectAdapter +``` + +## oracle-aggregator\src\events.rs + +### pub struct ProviderAdded + +```rust +pub struct ProviderAdded +``` + +### pub struct ProviderRemoved + +```rust +pub struct ProviderRemoved +``` + +### pub struct Aggregated + +```rust +pub struct Aggregated +``` + +### pub struct Fallback + +```rust +pub struct Fallback +``` + +## oracle-aggregator\src\fallback.rs + +### pub fn resolve_fallback + +/// The resolved fallback `(value, decimals, updated_at)`. +/// +/// `decimals` is always the caller's target precision (the last-good value is +/// already stored normalized, and the constant is defined in the default target +/// precision). + +```rust +pub fn resolve_fallback(last_good: Option<&LastGood>, target_decimals: u32) -> (i128, u32, u64) +``` + +## oracle-aggregator\src\health.rs + +### pub fn default_health + +/// A fresh, empty health record for `provider`. + +```rust +pub fn default_health(provider: &soroban_sdk::Address) -> ProviderHealth +``` + +### pub fn record_success + +/// Record a successful, fresh read at `now` returning `value`. + +```rust +pub fn record_success(health: &mut ProviderHealth, now: u64, value: i128) +``` + +### pub fn record_failure + +/// Record a failed read (cross-contract error or invalid value) at `now`. + +```rust +pub fn record_failure(health: &mut ProviderHealth, now: u64) +``` + +### pub fn record_stale + +/// Record a read rejected for staleness at `now`. + +```rust +pub fn record_stale(health: &mut ProviderHealth, now: u64) +``` + +### pub fn success_rate_bps + +/// Success rate as basis points (0 for a provider never read). + +```rust +pub fn success_rate_bps(health: &ProviderHealth) -> u32 +``` + +### pub fn is_healthy + +/// Whether the provider's most recent read succeeded. + +```rust +pub fn is_healthy(health: &ProviderHealth) -> bool +``` + +## oracle-aggregator\src\lib.rs + +### pub struct OracleAggregator + +```rust +pub struct OracleAggregator +``` + +### pub fn initialize + +/// Initialize the aggregator with an admin. Callable once. + +```rust +pub fn initialize(env: Env, admin: Address) +``` + +### pub fn add_provider + +/// Register a provider. Admin only. + +```rust +pub fn add_provider(env: Env, provider: ProviderConfig) +``` + +### pub fn remove_provider + +/// Remove a registered provider. Admin only. + +```rust +pub fn remove_provider(env: Env, provider: Address) +``` + +### pub fn set_config + +/// Replace the aggregation/validation policy. Admin only. + +```rust +pub fn set_config(env: Env, config: AggregationConfig) +``` + +### pub fn report + +/// Read every registered provider and produce the aggregated value. + /// + /// Each provider is read through its adapter, rejected if stale (vs. its own + /// `max_age_secs`) or invalid, and its health telemetry is updated. If at + /// least `min_confirmations` fresh reports survive, they are normalized to + /// `target_decimals`, outliers beyond `max_deviation_bps` are dropped, and + /// the median of the survivors is returned (and persisted as last-good). + /// Otherwise the fallback path is taken. + +```rust +pub fn report(env: Env) -> AggregationResult +``` + +### pub fn latest_answer + +/// The most recently aggregated value (fallback constant if none recorded). + +```rust +pub fn latest_answer(env: Env) -> i128 +``` + +### pub fn get_admin + +```rust +pub fn get_admin(env: Env) -> Option
+``` + +### pub fn get_config + +```rust +pub fn get_config(env: Env) -> AggregationConfig +``` + +### pub fn get_providers + +```rust +pub fn get_providers(env: Env) -> Vec +``` + +### pub fn get_last_good + +```rust +pub fn get_last_good(env: Env) -> Option +``` + +### pub fn get_health + +```rust +pub fn get_health(env: Env, provider: Address) -> Option +``` + +### pub fn get_health_summary + +/// Aggregate health across all registered providers. + +```rust +pub fn get_health_summary(env: Env) -> HealthSummary +``` + +## oracle-aggregator\src\storage.rs + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub fn encode + +/// Encode the key with the contract namespace prefix. + +```rust +pub fn encode(&self, env: &Env) -> Bytes +``` + +### pub fn get_admin + +```rust +pub fn get_admin(env: &Env) -> Option
+``` + +### pub fn set_admin + +```rust +pub fn set_admin(env: &Env, admin: &Address) +``` + +### pub fn get_config + +```rust +pub fn get_config(env: &Env) -> Option +``` + +### pub fn set_config + +```rust +pub fn set_config(env: &Env, config: &AggregationConfig) +``` + +### pub fn get_providers + +/// The registered providers, or an empty `Vec` if none have been added. + +```rust +pub fn get_providers(env: &Env) -> Vec +``` + +### pub fn set_providers + +```rust +pub fn set_providers(env: &Env, providers: &Vec) +``` + +### pub fn get_last_good + +```rust +pub fn get_last_good(env: &Env) -> Option +``` + +### pub fn set_last_good + +```rust +pub fn set_last_good(env: &Env, value: i128, decimals: u32, updated_at: u64) +``` + +### pub fn get_health + +```rust +pub fn get_health(env: &Env, provider: &Address) -> Option +``` + +### pub fn set_health + +```rust +pub fn set_health(env: &Env, provider: &Address, health: &ProviderHealth) +``` + +## oracle-aggregator\src\types.rs + +### pub enum AdapterKind + +```rust +pub enum AdapterKind +``` + +### pub struct OracleReport + +```rust +pub struct OracleReport +``` + +### pub struct ProviderConfig + +```rust +pub struct ProviderConfig +``` + +### pub struct AggregationConfig + +```rust +pub struct AggregationConfig +``` + +### pub struct AggregationResult + +```rust +pub struct AggregationResult +``` + +### pub struct LastGood + +```rust +pub struct LastGood +``` + +### pub struct ProviderHealth + +```rust +pub struct ProviderHealth +``` + +### pub struct HealthSummary + +```rust +pub struct HealthSummary +``` + +### pub enum Error + +```rust +pub enum Error +``` + +## price_oracle\src\enum.rs + +### pub struct PriceData + +```rust +pub struct PriceData +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub enum ContractError + +```rust +pub enum ContractError +``` + +### pub struct PriceOracle + +```rust +pub struct PriceOracle +``` + +### pub fn initialize + +/// Initialize the oracle with admin and updater addresses + +```rust +pub fn initialize( + env: Env, + admin: Address, + updater: Address, + initial_price: i128, + decimals: u32, + ) +``` + +### pub fn update_price + +/// Update the price (only callable by updater) + +```rust +pub fn update_price(env: Env, new_price: i128) +``` + +### pub fn get_price + +/// Get current price data + +```rust +pub fn get_price(env: Env) -> PriceData +``` + +### pub fn get_fresh_price + +/// Get price with staleness check + +```rust +pub fn get_fresh_price(env: Env) -> PriceData +``` + +### pub fn get_price_value + +/// Get just the price value + +```rust +pub fn get_price_value(env: Env) -> i128 +``` + +### pub fn get_decimals + +/// Get number of decimals + +```rust +pub fn get_decimals(env: Env) -> u32 +``` + +### pub fn xlm_to_usd_cents + +/// Convert XLM amount to USD cents + +```rust +pub fn xlm_to_usd_cents(env: Env, xlm_amount: i128) -> i128 +``` + +### pub fn usd_cents_to_xlm + +/// Convert USD cents to XLM amount + +```rust +pub fn usd_cents_to_xlm(env: Env, usd_cents: i128) -> i128 +``` + +### pub fn is_price_fresh + +/// Check if price is fresh + +```rust +pub fn is_price_fresh(env: Env) -> bool +``` + +### pub fn set_admin + +/// Admin functions + +```rust +pub fn set_admin(env: Env, new_admin: Address) +``` + +### pub fn set_updater + +```rust +pub fn set_updater(env: Env, new_updater: Address) +``` + +### pub fn get_admin + +/// Get admin address + +```rust +pub fn get_admin(env: Env) -> Address +``` + +### pub fn get_updater + +/// Get updater address + +```rust +pub fn get_updater(env: Env) -> Address +``` + +## price_oracle\src\lib.rs + +### pub struct PriceData + +```rust +pub struct PriceData +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub fn encode + +```rust +pub fn encode(&self, env: &Env) -> Bytes +``` + +### pub enum ContractError + +```rust +pub enum ContractError +``` + +### pub fn migrate_namespace + +/// Migrate storage entries from legacy (non-prefixed) keys to new namespaced keys. +/// Idempotent — safe to call multiple times. + +```rust +pub fn migrate_namespace(env: &Env) +``` + +### pub struct PriceOracle + +```rust +pub struct PriceOracle +``` + +### pub fn initialize + +/// Initialize the oracle with admin and updater addresses + +```rust +pub fn initialize( + env: Env, + admin: Address, + updater: Address, + initial_price: i128, + decimals: u32, + ) +``` + +### pub fn update_price + +/// Update the price (only callable by updater) + +```rust +pub fn update_price(env: Env, new_price: i128) +``` + +### pub fn get_price + +/// Get current price data + +```rust +pub fn get_price(env: Env) -> PriceData +``` + +### pub fn get_fresh_price + +/// Get price with staleness check + +```rust +pub fn get_fresh_price(env: Env) -> PriceData +``` + +### pub fn get_price_value + +/// Get just the price value + +```rust +pub fn get_price_value(env: Env) -> i128 +``` + +### pub fn get_decimals + +/// Get number of decimals + +```rust +pub fn get_decimals(env: Env) -> u32 +``` + +### pub fn xlm_to_usd_cents + +/// Convert XLM amount to USD cents + +```rust +pub fn xlm_to_usd_cents(env: Env, xlm_amount: i128) -> i128 +``` + +### pub fn usd_cents_to_xlm + +/// Convert USD cents to XLM amount + +```rust +pub fn usd_cents_to_xlm(env: Env, usd_cents: i128) -> i128 +``` + +### pub fn is_price_fresh + +/// Check if price is fresh + +```rust +pub fn is_price_fresh(env: Env) -> bool +``` + +### pub fn set_admin + +/// Admin functions + +```rust +pub fn set_admin(env: Env, new_admin: Address) +``` + +### pub fn set_updater + +```rust +pub fn set_updater(env: Env, new_updater: Address) +``` + +### pub fn get_admin + +/// Get admin address + +```rust +pub fn get_admin(env: Env) -> Address +``` + +### pub fn get_updater + +/// Get updater address + +```rust +pub fn get_updater(env: Env) -> Address +``` + +### pub fn migrate_namespace + +/// Migrate all storage entries from legacy (non-prefixed) keys to new namespaced keys. + /// Must be called by admin after a contract upgrade. + +```rust +pub fn migrate_namespace(env: Env) +``` + +## price_oracle\src\utility.rs + +### pub struct AssetShare + +```rust +pub struct AssetShare +``` + +### pub struct BasketStream + +```rust +pub struct BasketStream +``` + +### pub fn new + +```rust +pub fn new(owner: AccountId, assets: Vec<(String, u8)>, total_rate: u128) -> Self +``` + +### pub fn withdraw + +```rust +pub fn withdraw(&self, seconds: u128) -> Vec<(String, u128)> +``` + +### pub fn update_basket + +```rust +pub fn update_basket(&mut self, assets: Vec<(String, u8)>, total_rate: u128) +``` + +### pub fn get_basket + +```rust +pub fn get_basket(&self) -> Vec +``` + +## resource-token\src\admin.rs + +### pub fn set_admin + +/// Set the admin address +/// +/// This function can only be called once during initialization, or by the current admin. +/// +/// # Arguments +/// * `env` - The contract environment +/// * `new_admin` - The address to set as admin +/// +/// # Panics +/// * If an admin already exists and the caller is not the current admin + +```rust +pub fn set_admin(env: &Env, new_admin: Address) +``` + +### pub fn get_admin + +/// Get the current admin address +/// +/// # Arguments +/// * `env` - The contract environment +/// +/// # Returns +/// * `Some(Address)` if an admin is set +/// * `None` if no admin has been configured + +```rust +pub fn get_admin(env: &Env) -> Option
+``` + +### pub fn is_admin + +```rust +pub fn is_admin(env: &Env, address: &Address) -> bool +``` + +## resource-token\src\allowance.rs + +### pub fn approve + +/// Set allowance for a spender. +/// +/// # Warning +/// +/// This function is vulnerable to a race condition. If the owner changes the allowance +/// from N to M, a spender could potentially spend both N and M tokens if they +/// submit a transaction just before the allowance change. +/// Use `increase_allowance` and `decrease_allowance` to avoid this. + +```rust +pub fn approve(env: Env, owner: Address, spender: Address, amount: i128) +``` + +### pub fn increase_allowance + +/// Increase allowance for a spender. + +```rust +pub fn increase_allowance(env: Env, owner: Address, spender: Address, delta: i128) +``` + +### pub fn decrease_allowance + +/// Decrease allowance for a spender. + +```rust +pub fn decrease_allowance(env: Env, owner: Address, spender: Address, delta: i128) +``` + +### pub fn get_allowance + +/// Get allowance for a spender from an owner. + +```rust +pub fn get_allowance(env: Env, owner: Address, spender: Address) -> i128 +``` + +### pub fn transfer_from + +/// Transfer tokens from one address to another using an allowance. + +```rust +pub fn transfer_from(env: Env, spender: Address, from: Address, to: Address, amount: i128) +``` + +## resource-token\src\auth.rs + +### pub enum AuthError + +```rust +pub enum AuthError +``` + +### pub fn authorize_admin + +/// Authorize that the caller is the admin +/// +/// # Panics +/// * If no admin is set +/// * If caller is not the admin + +```rust +pub fn authorize_admin(env: &Env) +``` + +### pub fn authorize_mint + +/// Authorize mint operations with full call chain verification +/// +/// This function verifies that the admin has authorized the operation. +/// +/// # Arguments +/// * `env` - The contract environment +/// +/// # Panics +/// * If no admin is configured +/// * If admin has not authorized the operation + +```rust +pub fn authorize_mint(env: &Env) +``` + +### pub fn authorize_burn + +/// Authorize burn operations with full call chain verification +/// +/// This function verifies that the admin has authorized the operation. +/// +/// # Arguments +/// * `env` - The contract environment +/// +/// # Panics +/// * If no admin is configured +/// * If admin has not authorized the operation + +```rust +pub fn authorize_burn(env: &Env) +``` + +### pub fn check_operator_auth + +```rust +pub fn check_operator_auth(env: &Env, operator: &Address) -> bool +``` + +### pub fn authorize_with_operator + +```rust +pub fn authorize_with_operator(env: &Env, _operator: &Address) +``` + +## resource-token\src\lib.rs + +### pub struct ResourceToken + +```rust +pub struct ResourceToken +``` + +### pub fn initialize + +/// Initialize the contract with an admin + /// + /// # Arguments + /// * `env` - The contract environment + /// * `admin` - The address to set as admin + +```rust +pub fn initialize(env: Env, admin: Address) +``` + +### pub fn set_admin + +/// Set a new admin (only callable by current admin) + /// + /// # Arguments + /// * `env` - The contract environment + /// * `new_admin` - The address to set as the new admin + +```rust +pub fn set_admin(env: Env, new_admin: Address) +``` + +### pub fn get_admin + +/// Get the current admin address + /// + /// # Returns + /// * `Some(Address)` if admin is set, `None` otherwise + +```rust +pub fn get_admin(env: Env) -> Option
+``` + +### pub fn authorize_operator + +/// Authorize an operator to perform mint/burn operations + /// + /// # Arguments + /// * `env` - The contract environment + /// * `operator` - The address to authorize + /// * `expiration` - Unix timestamp when authorization expires (max 30 days) + /// + /// # Panics + /// * If caller is not admin + /// * If expiration is in the past or too far in the future + +```rust +pub fn authorize_operator(env: Env, operator: Address, expiration: u64) +``` + +### pub fn revoke_operator + +/// Revoke operator authorization + /// + /// # Arguments + /// * `env` - The contract environment + /// * `operator` - The address to revoke + /// + /// # Panics + /// * If caller is not admin + +```rust +pub fn revoke_operator(env: Env, operator: Address) +``` + +### pub fn is_valid_operator + +/// Check if an address is a valid (non-expired) operator + /// + /// # Arguments + /// * `env` - The contract environment + /// * `operator` - The address to check + /// + /// # Returns + /// * `true` if operator is authorized and not expired + +```rust +pub fn is_valid_operator(env: Env, operator: Address) -> bool +``` + +### pub fn mint + +/// Mint tokens to an address + /// + /// This operation requires full authorization via call chain verification. + /// Only the admin or a valid operator can mint tokens. + /// + /// # Arguments + /// * `env` - The contract environment + /// * `to` - The address to mint tokens to + /// * `amount` - The amount of tokens to mint + /// + /// # Panics + /// * If caller is not authorized (not admin or valid operator) + /// * If amount is negative or zero + /// * If the mint would push `total_supply` above `MAX_SUPPLY` + /// * If call chain depth is exceeded + +```rust +pub fn mint(env: Env, to: Address, amount: i128) +``` + +### pub fn burn + +/// Burn tokens from an address + /// + /// This operation requires full authorization via call chain verification. + /// Only the admin or a valid operator can burn tokens. + /// + /// # Arguments + /// * `env` - The contract environment + /// * `from` - The address to burn tokens from + /// * `amount` - The amount of tokens to burn + /// + /// # Panics + /// * If caller is not authorized (not admin or valid operator) + /// * If amount is negative or zero + /// * If insufficient balance + /// * If call chain depth is exceeded + +```rust +pub fn burn(env: Env, from: Address, amount: i128) +``` + +### pub fn balance + +/// Get the balance of an address + /// + /// # Arguments + /// * `env` - The contract environment + /// * `address` - The address to query + /// + /// # Returns + /// * The token balance of the address + +```rust +pub fn balance(env: Env, address: Address) -> i128 +``` + +### pub fn total_supply + +/// Get the total supply of tokens + /// + /// # Returns + /// * The total supply of tokens + +```rust +pub fn total_supply(env: Env) -> i128 +``` + +### pub fn migrate_namespace + +/// Migrate all storage entries from legacy (non-prefixed) keys to new namespaced keys. + /// Must be called by the admin after a contract upgrade. + +```rust +pub fn migrate_namespace(env: Env, addresses: Vec
) +``` + +### pub fn approve + +/// Set allowance for a spender + +```rust +pub fn approve(env: Env, owner: Address, spender: Address, amount: i128) +``` + +### pub fn increase_allowance + +/// Increase allowance for a spender + +```rust +pub fn increase_allowance(env: Env, owner: Address, spender: Address, delta: i128) +``` + +### pub fn decrease_allowance + +/// Decrease allowance for a spender + +```rust +pub fn decrease_allowance(env: Env, owner: Address, spender: Address, delta: i128) +``` + +### pub fn allowance + +/// Get allowance for a spender + +```rust +pub fn allowance(env: Env, owner: Address, spender: Address) -> i128 +``` + +### pub fn transfer_from + +/// Transfer tokens using an allowance + +```rust +pub fn transfer_from(env: Env, spender: Address, from: Address, to: Address, amount: i128) +``` + +## resource-token\src\operators.rs + +### pub fn authorize_operator + +/// Authorize an operator to perform mint/burn operations until the expiration timestamp +/// +/// # Arguments +/// * `env` - The contract environment +/// * `operator` - The address to authorize as an operator +/// * `expiration` - Unix timestamp when the delegation expires +/// +/// # Panics +/// * If caller is not the admin +/// * If expiration is in the past or zero + +```rust +pub fn authorize_operator(env: &Env, operator: Address, expiration: u64) +``` + +### pub fn revoke_operator + +/// Revoke operator authorization +/// +/// # Arguments +/// * `env` - The contract environment +/// * `operator` - The address to revoke authorization from +/// +/// # Panics +/// * If caller is not the admin + +```rust +pub fn revoke_operator(env: &Env, operator: Address) +``` + +### pub fn is_valid_operator + +/// Check if an address is a valid operator (not expired) +/// +/// # Arguments +/// * `env` - The contract environment +/// * `operator` - The address to check +/// +/// # Returns +/// * `true` if the operator is authorized and not expired, `false` otherwise + +```rust +pub fn is_valid_operator(env: &Env, operator: &Address) -> bool +``` + +## resource-token\src\storage.rs + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub fn encode + +```rust +pub fn encode(&self, env: &Env) -> Bytes +``` + +### pub fn get_admin + +/// Get the admin address from storage + +```rust +pub fn get_admin(env: &Env) -> Option
+``` + +### pub fn set_admin + +/// Set the admin address in storage + +```rust +pub fn set_admin(env: &Env, admin: &Address) +``` + +### pub fn get_operator_expiration + +/// Get operator delegation expiration timestamp + +```rust +pub fn get_operator_expiration(env: &Env, operator: &Address) -> Option +``` + +### pub fn set_operator + +/// Set operator delegation with expiration + +```rust +pub fn set_operator(env: &Env, operator: &Address, expiration: u64) +``` + +### pub fn remove_operator + +/// Remove operator delegation + +```rust +pub fn remove_operator(env: &Env, operator: &Address) +``` + +### pub fn get_nonce + +/// Get nonce for an address + +```rust +pub fn get_nonce(env: &Env, address: &Address) -> u64 +``` + +### pub fn increment_nonce + +/// Increment and return the new nonce for an address + +```rust +pub fn increment_nonce(env: &Env, address: &Address) -> u64 +``` + +### pub fn get_total_supply + +/// Get total supply + +```rust +pub fn get_total_supply(env: &Env) -> i128 +``` + +### pub fn set_total_supply + +/// Set total supply + +```rust +pub fn set_total_supply(env: &Env, supply: i128) +``` + +### pub fn get_balance + +/// Get balance for an address + +```rust +pub fn get_balance(env: &Env, address: &Address) -> i128 +``` + +### pub fn set_balance + +/// Set balance for an address + +```rust +pub fn set_balance(env: &Env, address: &Address, balance: i128) +``` + +### pub fn get_allowance + +/// Get allowance for a spender from an owner + +```rust +pub fn get_allowance(env: &Env, owner: &Address, spender: &Address) -> i128 +``` + +### pub fn set_allowance + +/// Set allowance for a spender from an owner + +```rust +pub fn set_allowance(env: &Env, owner: &Address, spender: &Address, amount: i128) +``` + +### pub fn migrate_namespace + +/// Migrate all storage entries from legacy (non-prefixed) keys to new namespaced keys. +/// Idempotent — safe to call multiple times. + +```rust +pub fn migrate_namespace(env: &Env, addresses: &SdkVec
) +``` + +## settlement\src\conversion.rs + +### pub fn convert_to_settlement_currency + +/// Convert resource token volume to settlement currency using the supplied +/// (already staleness-resolved) exchange rate. +/// +/// The `rate` is resolved by the caller via +/// [`crate::rate_application::resolve_rate`], so a stale oracle has already been +/// replaced by the conservative fallback before reaching this function. +/// +/// Flow: +/// 1. Computes the settlement amount = volume * rate / 1e7 +/// 2. Checks actual amount against slippage tolerance and user's minimum +/// +/// # Returns +/// The settlement amount computed from the resolved rate +/// +/// # Panics +/// * `SlippageExceeded` if slippage exceeds MAX_SLIPPAGE_BPS or actual < min_expected_amount + +```rust +pub fn convert_to_settlement_currency( + env: &Env, + rate: i128, + volume: i128, + min_expected_amount: Option, +) -> i128 +``` + +## settlement\src\fees.rs + +### pub fn compute_fee + +```rust +pub fn compute_fee(env: &Env, amount: i128, rate_bps: u32) -> i128 +``` + +## settlement\src\lib.rs + +### pub struct OraclePrice + +```rust +pub struct OraclePrice +``` + +### pub enum SettlementError + +```rust +pub enum SettlementError +``` + +### pub struct SettlementContract + +```rust +pub struct SettlementContract +``` + +### pub fn settle + +/// Settle a payment and collect the protocol fee. + /// + /// # Arguments + /// * `env` - Contract environment + /// * `token` - Token contract address + /// * `payer` - Address paying the settlement + /// * `payee` - Address receiving the net settlement + /// * `fee_collector` - Address collecting the protocol fee + /// * `amount` - Gross settlement amount + /// * `rate_bps` - Fee rate in basis points + /// + /// # Returns + /// (net_amount, fee_amount) + +```rust +pub fn settle( + env: Env, + token: Address, + payer: Address, + payee: Address, + fee_collector: Address, + amount: i128, + rate_bps: u32, + ) -> (i128, i128) +``` + +### pub fn calculate_fee + +/// Compute the fee for a given amount and rate (pure, no side effects). + +```rust +pub fn calculate_fee(env: Env, amount: i128, rate_bps: u32) -> i128 +``` + +### pub fn finalize_settlement + +/// Finalize settlement with oracle-based currency conversion and slippage protection. + /// + /// Converts resource token volume to settlement currency using the current + /// oracle exchange rate, with both protocol-enforced and user-defined slippage bounds. + /// Fee is deducted from the settlement amount before transfer. + /// + /// # Arguments + /// * `env` - Contract environment + /// * `oracle` - Address of the price oracle contract + /// * `payer` - Address funding the settlement + /// * `fee_collector` - Address collecting the protocol fee + /// * `args` - Settlement parameters (token, volume, recipient, min_expected_amount) + /// * `rate_bps` - Fee rate in basis points + /// + /// # Returns + /// SettlementResult containing net_amount, fee_amount, and rate_used + +```rust +pub fn finalize_settlement( + env: Env, + oracle: Address, + payer: Address, + fee_collector: Address, + args: SettlementArgs, + rate_bps: u32, + ) -> SettlementResult +``` + +## settlement\src\rate_application.rs + +### pub fn is_stale + +/// Whether a price stamped at `last_updated` is stale relative to `now`. +/// +/// Staleness is `age > MAX_ORACLE_AGE`; a price exactly `MAX_ORACLE_AGE` seconds +/// old is still considered fresh (boundary is inclusive of "fresh"). Uses +/// saturating subtraction so a `last_updated` in the (clock-skewed) future is +/// treated as age 0 / fresh rather than underflowing. + +```rust +pub fn is_stale(now: u64, last_updated: u64) -> bool +``` + +### pub fn compute_fallback_rate + +/// The conservative rate used when the oracle price is stale. + +```rust +pub fn compute_fallback_rate() -> i128 +``` + +### pub fn apply_rate_to_volume + +/// Apply a 7-decimal fixed-point `rate` to `volume`: `volume * rate / 1e7`. +/// Overflow-checked. + +```rust +pub fn apply_rate_to_volume(env: &Env, volume: i128, rate: i128) -> i128 +``` + +### pub fn get_fresh_rate + +/// Fetch the current oracle rate, **rejecting** stale data. +/// +/// Returns `Err(SettlementError::OracleStale)` if the oracle's price is older +/// than [`MAX_ORACLE_AGE`]. This is the strict, halt-on-stale variant referenced +/// by issue #7 step 7 (propagate a `Result` instead of panicking). + +```rust +pub fn get_fresh_rate(env: &Env, oracle: &Address) -> Result +``` + +### pub fn resolve_rate + +/// Resolve the rate to use for settlement: the fresh oracle price when +/// available, otherwise the conservative [`FALLBACK_RATE`]. +/// +/// On fallback a `StaleFbk` event `(now, last_updated, fallback_rate)` is emitted +/// so downstream monitors can detect that the stale-price protection engaged. + +```rust +pub fn resolve_rate(env: &Env, oracle: &Address) -> i128 +``` + +## settlement\src\reentrancy.rs + +### pub struct ReentrancyGuard<'a> + +/// RAII reentrancy mutex. Acquire at the top of every externally-callable entry +/// point that performs cross-contract calls. + +```rust +pub struct ReentrancyGuard<'a> +``` + +### pub fn new + +/// Acquire the lock. Panics with [`SettlementError::ReentrantCall`] if it is + /// already held — i.e. if this is a reentrant call. + +```rust +pub fn new(env: &'a Env) -> Self +``` + +## settlement\src\storage.rs + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +## settlement\src\token_utils.rs + +### pub fn collect_fee + +/// Transfer the protocol fee from payer to the fee collector. +/// +/// # Arguments +/// * `env` - Contract environment +/// * `token` - Address of the token contract +/// * `payer` - Address paying the fee +/// * `fee_collector` - Address receiving the fee +/// * `amount` - Settlement amount (gross, before fee deduction) +/// * `rate_bps` - Fee rate in basis points +/// +/// # Returns +/// The fee amount that was transferred + +```rust +pub fn collect_fee( + env: &Env, + token: &Address, + payer: &Address, + fee_collector: &Address, + amount: i128, + rate_bps: u32, +) -> i128 +``` + +### pub fn verify_fee_invariant + +```rust +pub fn verify_fee_invariant(amount: i128, rate_bps: u32, fee: i128) -> bool +``` + +## settlement\src\types.rs + +### pub struct SettlementArgs + +```rust +pub struct SettlementArgs +``` + +### pub struct SettlementResult + +```rust +pub struct SettlementResult +``` + +## utility_contracts\src\asset.rs + +### pub struct AutoRefill + +```rust +pub struct AutoRefill +``` + +### pub fn new + +```rust +pub fn new(owner: AccountId, vault: AccountId, stable_asset: String, min_balance: u128) -> Self +``` + +### pub fn check_and_refill + +```rust +pub fn check_and_refill(&mut self, current_balance: u128) -> Result<(), String> +``` + +### pub fn set_min_balance + +```rust +pub fn set_min_balance(&mut self, new_threshold: u128) -> Result<(), String> +``` + +## utility_contracts\src\auto_refill.rs + +### pub struct AutoRefill + +```rust +pub struct AutoRefill +``` + +### pub fn new + +```rust +pub fn new(owner: AccountId, vault: AccountId, stable_asset: String, min_balance: u128) -> Self +``` + +### pub fn check_and_refill + +```rust +pub fn check_and_refill(&mut self, current_balance: u128) -> Result<(), String> +``` + +### pub fn set_min_balance + +```rust +pub fn set_min_balance(&mut self, new_threshold: u128) -> Result<(), String> +``` + +## utility_contracts\src\basket_stream.rs + +### pub struct AssetShare + +```rust +pub struct AssetShare +``` + +### pub struct BasketStream + +```rust +pub struct BasketStream +``` + +### pub fn new + +```rust +pub fn new(owner: AccountId, assets: Vec<(String, u8)>, total_rate: u128) -> Self +``` + +### pub fn withdraw + +```rust +pub fn withdraw(&self, seconds: u128) -> Vec<(String, u128)> +``` + +### pub fn update_basket + +```rust +pub fn update_basket(&mut self, assets: Vec<(String, u8)>, total_rate: u128) +``` + +### pub fn get_basket + +```rust +pub fn get_basket(&self) -> Vec +``` + +## utility_contracts\src\energy_grid.rs + +### pub struct LoadConfig + +```rust +pub struct LoadConfig +``` + +### pub fn set_peak_multiplier + +```rust +pub fn set_peak_multiplier(env: &Env, admin: Address, multiplier: i128) +``` + +### pub fn set_low_discount + +```rust +pub fn set_low_discount(env: &Env, admin: Address, discount: i128) +``` + +### pub fn bill_consumption + +```rust +pub fn bill_consumption(env: &Env, user: Address, base_rate: i128, timestamp: u64) -> i128 +``` + +## utility_contracts\src\enterprise.rs + +### pub struct FleetState + +```rust +pub struct FleetState +``` + +### pub struct FleetLimitUpdatedEvent + +```rust +pub struct FleetLimitUpdatedEvent +``` + +### pub fn fleet_get_active_sum + +```rust +pub fn fleet_get_active_sum(env: &Env, provider: &Address) -> i128 +``` + +### pub fn fleet_get_cap + +```rust +pub fn fleet_get_cap(env: &Env, provider: &Address) -> i128 +``` + +### pub fn fleet_apply_delta + +/// Atomically applies delta (may be negative) to fleet aggregate using saturating arithmetic. + +```rust +pub fn fleet_apply_delta(env: &Env, provider: &Address, delta: i128) +``` + +### pub fn fleet_assert_room_for_new_stream + +```rust +pub fn fleet_assert_room_for_new_stream(env: &Env, provider: &Address, new_stream_rate: i128) +``` + +### pub fn set_fleet_cap_super_admin + +```rust +pub fn set_fleet_cap_super_admin(env: &Env, provider: Address, new_cap: i128, admin: Address) +``` + +### pub struct P2PExchangeFinalizedEvent + +```rust +pub struct P2PExchangeFinalizedEvent +``` + +### pub enum P2PRole + +```rust +pub enum P2PRole +``` + +### pub fn p2p_net_flow_amount + +/// Fixed-point friendly net over `delta_seconds`: supply_rate and demand_rate are tokens/sec (same unit as streams). + +```rust +pub fn p2p_net_flow_amount(supply_rate: i128, demand_rate: i128, delta_seconds: i128) -> i128 +``` + +### pub fn p2p_finalize_exchange + +```rust +pub fn p2p_finalize_exchange( + env: &Env, + supplier: Address, + consumer: Address, + utility_treasury: Address, + supply_rate: i128, + demand_rate: i128, + delta_seconds: i128, + grid_fee_bps: i128, + battery_credit_cap: i128, + consumer_token: &Address, +) -> (i128, i128) +``` + +### pub struct DeviceLivenessBreachedEvent + +```rust +pub struct DeviceLivenessBreachedEvent +``` + +### pub fn stream_heartbeat + +```rust +pub fn stream_heartbeat( + env: &Env, + stream_id: u64, + meter_id: u64, + signature: BytesN<64>, + pub_key: BytesN<32>, +) +``` + +### pub fn liveness_check_and_slash + +/// Called from settlement paths: if heartbeat stale, slash buffer proportionally and optionally mark unreliable. + +```rust +pub fn liveness_check_and_slash( + env: &Env, + stream_id: u64, + meter_id: u64, + stale_threshold_ledgers: u32, +) -> i128 +``` + +### pub fn pardon_liveness_slash + +```rust +pub fn pardon_liveness_slash(env: &Env, stream_id: u64, provider: Address) +``` + +### pub enum PriorityTier + +```rust +pub enum PriorityTier +``` + +### pub struct ProviderGridEpoch + +```rust +pub struct ProviderGridEpoch +``` + +### pub struct GridShortageAlertEvent + +```rust +pub struct GridShortageAlertEvent +``` + +### pub fn tier_rank + +```rust +pub fn tier_rank(tier: PriorityTier) -> u32 +``` + +### pub fn provider_grid_state + +```rust +pub fn provider_grid_state(env: &Env, provider: &Address) -> ProviderGridEpoch +``` + +### pub fn global_load_shed + +```rust +pub fn global_load_shed( + env: &Env, + provider: Address, + minimum_surviving_tier: PriorityTier, + grid_admin: Address, +) +``` + +### pub fn stream_should_grid_pause + +```rust +pub fn stream_should_grid_pause(flow: &ContinuousFlow, grid: &ProviderGridEpoch) -> bool +``` + +### pub fn stream_acknowledge_grid_epoch + +```rust +pub fn stream_acknowledge_grid_epoch(env: &Env, stream_id: u64, flow: &mut ContinuousFlow) +``` + +## utility_contracts\src\gasless_relay.rs + +### pub enum GaslessRelayError + +```rust +pub enum GaslessRelayError +``` + +### pub struct MetaTxRequest + +```rust +pub struct MetaTxRequest +``` + +### pub struct SponsorshipPolicy + +```rust +pub struct SponsorshipPolicy +``` + +### pub struct RateLimitTracker + +```rust +pub struct RateLimitTracker +``` + +### pub struct ForwarderConfig + +```rust +pub struct ForwarderConfig +``` + +### pub struct GaslessRelay + +```rust +pub struct GaslessRelay +``` + +### pub fn initialize + +/// Initialize the gasless relay contract + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `admin` - Administrator address with permission to configure the relay + /// * `sponsorship_pool_balance` - Initial XLM balance for sponsoring transactions + /// + /// # Returns + /// A success indicator or error + +```rust +pub fn initialize(env: Env, admin: Address, sponsorship_pool_balance: i128) -> Result<(), u32> +``` + +### pub fn register_forwarder + +/// Register a trusted forwarder + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `forwarder_address` - Address of the forwarder contract + /// * `public_key` - Public key for signature verification + /// + /// # Returns + /// A success indicator or error + +```rust +pub fn register_forwarder( + env: Env, + forwarder_address: Address, + public_key: BytesN<32>, + ) -> Result<(), u32> +``` + +### pub fn register_sponsorship_policy + +/// Register a sponsorship policy for an operation + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - Identifier for the operation type + /// * `policy` - Sponsorship policy configuration + /// + /// # Returns + /// A success indicator or error + +```rust +pub fn register_sponsorship_policy( + env: Env, + operation_id: Symbol, + policy: SponsorshipPolicy, + ) -> Result<(), u32> +``` + +### pub fn set_rate_limit + +/// Set rate limit for sponsored transactions + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `user` - User address + /// * `max_transactions_per_period` - Maximum sponsored transactions + /// * `period_seconds` - Rate limit period in seconds + /// + /// # Returns + /// A success indicator or error + +```rust +pub fn set_rate_limit( + env: Env, + user: Address, + max_transactions_per_period: u32, + period_seconds: u64, + ) -> Result<(), u32> +``` + +### pub fn forward_meta_transaction + +/// Forward a meta-transaction from a trusted forwarder + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `request` - The meta-transaction request + /// * `signature` - The signature from the forwarder + /// + /// # Returns + /// The result of the forwarded call + +```rust +pub fn forward_meta_transaction( + env: Env, + request: MetaTxRequest, + signature: Bytes, + ) -> Result +``` + +### pub fn get_nonce + +/// Get the current nonce for a user + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `user` - User address + /// + /// # Returns + /// The current nonce value + +```rust +pub fn get_nonce(env: Env, user: Address) -> u64 +``` + +### pub fn get_sponsorship_pool_balance + +/// Get sponsorship pool balance + /// + /// # Arguments + /// * `env` - The Soroban environment + /// + /// # Returns + /// The current sponsorship pool balance in stroops + +```rust +pub fn get_sponsorship_pool_balance(env: Env) -> i128 +``` + +### pub fn top_up_sponsorship_pool + +/// Top up the sponsorship pool + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `amount` - Amount to add to the pool in stroops + /// + /// # Returns + /// A success indicator or error + +```rust +pub fn top_up_sponsorship_pool(env: Env, amount: i128) -> Result<(), u32> +``` + +## utility_contracts\src\gasless_relay_policy.rs + +### pub enum SponsorshipStatus + +```rust +pub enum SponsorshipStatus +``` + +### pub struct DetailedSponsorshipPolicy + +```rust +pub struct DetailedSponsorshipPolicy +``` + +### pub struct OperationStats + +```rust +pub struct OperationStats +``` + +### pub enum EligibilityCheckResult + +```rust +pub enum EligibilityCheckResult +``` + +### pub struct SponsorshipPolicyEngine + +```rust +pub struct SponsorshipPolicyEngine +``` + +### pub fn init_policy_engine + +/// Initialize the policy engine + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `admin` - Initial administrator + +```rust +pub fn init_policy_engine(env: Env, admin: Address) -> Result<(), u32> +``` + +### pub fn create_policy + +/// Create a new sponsorship policy + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation identifier + /// * `policy` - The policy details + +```rust +pub fn create_policy( + env: Env, + operation_id: Symbol, + policy: DetailedSponsorshipPolicy, + ) -> Result<(), u32> +``` + +### pub fn update_policy + +/// Update an existing policy + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation to update + /// * `new_policy` - The updated policy + +```rust +pub fn update_policy( + env: Env, + operation_id: Symbol, + new_policy: DetailedSponsorshipPolicy, + ) -> Result<(), u32> +``` + +### pub fn check_eligibility + +/// Check if an operation is eligible for sponsorship + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation to check + /// * `gas_needed` - The gas required for the operation + /// * `pool_balance` - The current sponsorship pool balance + +```rust +pub fn check_eligibility( + env: Env, + operation_id: Symbol, + gas_needed: u64, + pool_balance: i128, + ) -> EligibilityCheckResult +``` + +### pub fn get_policy + +/// Get policy details + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation to get details for + +```rust +pub fn get_policy( + env: Env, + operation_id: Symbol, + ) -> Option +``` + +### pub fn get_operation_stats + +/// Get operation statistics + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation to get stats for + +```rust +pub fn get_operation_stats(env: Env, operation_id: Symbol) -> Option +``` + +### pub fn record_sponsored_transaction + +/// Record a sponsored transaction + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation that was sponsored + /// * `gas_used` - The actual gas used + /// * `cost` - The cost incurred + +```rust +pub fn record_sponsored_transaction( + env: Env, + operation_id: Symbol, + gas_used: u64, + cost: i128, + ) -> Result<(), u32> +``` + +### pub fn suspend_policy + +/// Suspend a sponsorship policy + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation to suspend + +```rust +pub fn suspend_policy(env: Env, operation_id: Symbol) -> Result<(), u32> +``` + +### pub fn resume_policy + +/// Resume a suspended policy + /// + /// # Arguments + /// * `env` - The Soroban environment + /// * `operation_id` - The operation to resume + /// * `new_status` - The new status (FullySponsored or PartiallySponsored) + +```rust +pub fn resume_policy(env: Env, operation_id: Symbol, new_status: SponsorshipStatus) -> Result<(), u32> +``` + +### pub fn list_active_policies + +/// List all active policies + /// + /// # Arguments + /// * `env` - The Soroban environment + +```rust +pub fn list_active_policies(env: Env) -> Vec +``` + +## utility_contracts\src\gasless_relay_sig_verify.rs + +### pub enum SignatureVerificationResult + +```rust +pub enum SignatureVerificationResult +``` + +### pub fn verify_meta_transaction_signature + +/// Verify a meta-transaction signature using Ed25519 +/// +/// # Arguments +/// * `env` - The Soroban environment +/// * `request_data` - The encoded meta-transaction request data +/// * `signature` - The Ed25519 signature bytes +/// * `public_key` - The signer's public key +/// * `timestamp` - The timestamp of signature generation +/// +/// # Returns +/// SignatureVerificationResult indicating validity + +```rust +pub fn verify_meta_transaction_signature( + env: &Env, + request_data: &[u8], + signature: &BytesN<64>, + public_key: &BytesN<32>, + timestamp: u64, +) -> SignatureVerificationResult +``` + +### pub fn is_approved_forwarder + +/// Verify that the caller is an approved forwarder +/// +/// # Arguments +/// * `env` - The Soroban environment +/// * `caller` - The address claiming to be a forwarder +/// * `approved_forwarders` - List of approved forwarder addresses +/// +/// # Returns +/// true if the caller is an approved forwarder + +```rust +pub fn is_approved_forwarder( + env: &Env, + caller: &Address, + approved_forwarders: &Vec
, +) -> bool +``` + +### pub fn hash_meta_transaction_request + +/// Hash meta-transaction data for signature verification (EIP-2771 compatible) +/// +/// # Arguments +/// * `env` - The Soroban environment +/// * `from` - The originating address +/// * `to` - The target contract address +/// * `value` - The value in stroops +/// * `data` - The encoded function call data +/// * `nonce` - The nonce for replay protection +/// * `gas_price` - The gas price +/// * `gas_limit` - The gas limit +/// * `deadline` - The transaction deadline +/// +/// # Returns +/// A 32-byte hash of the meta-transaction request + +```rust +pub fn hash_meta_transaction_request( + env: &Env, + from: &Address, + to: &Address, + value: i128, + data: &[u8], + nonce: u64, + gas_price: i128, + gas_limit: u64, + deadline: u64, +) -> BytesN<32> +``` + +### pub fn validate_signer_address + +/// Validate that a signature matches the expected forwarder +/// +/// # Arguments +/// * `env` - The Soroban environment +/// * `signer_address` - The address that should have signed +/// * `expected_signer_public_key` - The public key of the expected signer +/// * `signature` - The signature to verify +/// +/// # Returns +/// true if signature is valid and matches the expected signer + +```rust +pub fn validate_signer_address( + env: &Env, + signer_address: &Address, + expected_signer_public_key: &BytesN<32>, + signature: &BytesN<64>, +) -> bool +``` + +### pub fn validate_forwarder_nonce + +/// Check if a forwarder signature has valid nonce to prevent replay attacks +/// +/// # Arguments +/// * `env` - The Soroban environment +/// * `forwarder` - The forwarder address +/// * `request_nonce` - The nonce in the current request +/// * `last_nonce` - The last used nonce for this forwarder +/// +/// # Returns +/// true if the nonce is valid and sequential + +```rust +pub fn validate_forwarder_nonce( + _env: &Env, + _forwarder: &Address, + request_nonce: u64, + last_nonce: u64, +) -> bool +``` + +### pub fn recover_signer_from_signature + +/// Recover the signer's address from a signature (EIP-2771 style) +/// +/// # Arguments +/// * `env` - The Soroban environment +/// * `message_hash` - The hash of the message that was signed +/// * `signature` - The signature bytes +/// +/// # Returns +/// The recovered address, or error if recovery fails + +```rust +pub fn recover_signer_from_signature( + env: &Env, + message_hash: &BytesN<32>, + signature: &BytesN<64>, +) -> Result +``` + +### pub fn validate_request_structure + +/// Verify that the meta-transaction request structure is valid +/// +/// # Arguments +/// * `_env` - The Soroban environment +/// * `from` - The from address +/// * `to` - The to address +/// * `deadline` - The deadline timestamp +/// +/// # Returns +/// true if the request structure is valid + +```rust +pub fn validate_request_structure( + _env: &Env, + from: &Address, + to: &Address, + _deadline: u64, +) -> bool +``` + +## utility_contracts\src\gas_estimator.rs + +### pub struct GasCostEstimator + +```rust +pub struct GasCostEstimator +``` + +### pub fn estimate_meter_monthly_cost + +```rust +pub fn estimate_meter_monthly_cost( + _env: &Env, + is_group_meter: bool, + _meters_in_group: u32, + ) -> i128 +``` + +### pub fn estimate_provider_monthly_cost + +/// `percentage_group_meters_bps`: percentage in basis points (10000 = 100%) + +```rust +pub fn estimate_provider_monthly_cost( + _env: &Env, + number_of_meters: u32, + percentage_group_meters_bps: i128, // basis points (10000 = 100%) + ) -> i128 +``` + +### pub fn estimate_large_scale_costs + +```rust +pub fn estimate_large_scale_costs( + env: &Env, + number_of_meters: u32, + percentage_group_meters_bps: i128, + ) -> LargeScaleCostEstimate +``` + +### pub fn get_operation_cost + +```rust +pub fn get_operation_cost(operation: &soroban_sdk::String) -> i128 +``` + +### pub struct LargeScaleCostEstimate + +```rust +pub struct LargeScaleCostEstimate +``` + +## utility_contracts\src\gas_metrics.rs + +### pub struct GasBaseline + +/// Baseline gas costs in stroops (for reference and comparison) + +```rust +pub struct GasBaseline +``` + +### pub struct GasMeasurement + +```rust +pub struct GasMeasurement +``` + +### pub fn efficiency_ratio + +```rust +pub fn efficiency_ratio(&self) -> f64 +``` + +### pub fn gas_variance + +```rust +pub fn gas_variance(&self) -> i128 +``` + +### pub fn variance_percentage + +```rust +pub fn variance_percentage(&self) -> f64 +``` + +### pub fn is_within_tolerance + +```rust +pub fn is_within_tolerance(&self, tolerance_percent: f64) -> bool +``` + +### pub struct GasStatistics + +```rust +pub struct GasStatistics +``` + +### pub fn efficiency_ratio + +```rust +pub fn efficiency_ratio(&self) -> f64 +``` + +### pub fn variance_percentage + +```rust +pub fn variance_percentage(&self) -> f64 +``` + +### pub struct GasMeter + +/// Global gas meter for collecting metrics across all tests + +```rust +pub struct GasMeter +``` + +### pub fn record_measurement + +/// Record a gas measurement + +```rust +pub fn record_measurement( + &self, + operation_name: impl Into, + estimated_gas: i128, + actual_gas: i128, + ) +``` + +### pub fn push_test + +/// Begin a test context + +```rust +pub fn push_test(&self, test_name: impl Into) +``` + +### pub fn pop_test + +/// End a test context + +```rust +pub fn pop_test(&self) +``` + +### pub fn get_measurements + +/// Get all measurements + +```rust +pub fn get_measurements(&self) -> Vec +``` + +### pub fn get_operation_measurements + +/// Get measurements for a specific operation + +```rust +pub fn get_operation_measurements(&self, operation_name: &str) -> Vec +``` + +### pub fn get_operation_statistics + +/// Calculate statistics for an operation + +```rust +pub fn get_operation_statistics(&self, operation_name: &str) -> Option +``` + +### pub fn get_all_statistics + +/// Get statistics for all operations + +```rust +pub fn get_all_statistics(&self) -> BTreeMap +``` + +### pub fn get_expensive_operations + +/// Get measurements exceeding a gas threshold + +```rust +pub fn get_expensive_operations(&self, threshold: i128) -> Vec +``` + +### pub fn get_deviations + +/// Get measurements deviating from estimates + +```rust +pub fn get_deviations(&self, tolerance_percent: f64) -> Vec +``` + +### pub fn clear + +/// Clear all measurements + +```rust +pub fn clear(&self) +``` + +### pub fn generate_report + +/// Generate a summary report + +```rust +pub fn generate_report(&self) -> GasReport +``` + +### pub struct GasReport + +```rust +pub struct GasReport +``` + +### pub fn print_summary + +```rust +pub fn print_summary(&self) +``` + +### pub fn print_detailed_report + +```rust +pub fn print_detailed_report(&self) +``` + +### pub struct TestGasGuard + +/// Guard to automatically manage test context + +```rust +pub struct TestGasGuard +``` + +### pub fn new + +```rust +pub fn new(test_name: impl Into) -> Self +``` + +### pub fn measure_gas + +/// Measure gas for a closure + +```rust +pub fn measure_gas(operation_name: impl Into, estimated: i128, f: F) -> T +where + F: FnOnce() -> T, +``` + +### pub struct GasBenchmark + +/// Compare two implementations and report gas differences + +```rust +pub struct GasBenchmark +``` + +### pub fn improvement_percent + +```rust +pub fn improvement_percent(&self) -> f64 +``` + +### pub fn print_comparison + +```rust +pub fn print_comparison(&self) +``` + +### pub fn get_gas_hotspots + +/// Get the gas hotspots (most expensive operations) + +```rust +pub fn get_gas_hotspots(limit: usize) -> Vec<(String, i128)> +``` + +### pub fn validate_gas_constraints + +/// Check if gas usage is within acceptable ranges + +```rust +pub fn validate_gas_constraints(constraints: &GasConstraints) -> GasValidationResult +``` + +### pub struct GasConstraints + +/// Gas constraints configuration + +```rust +pub struct GasConstraints +``` + +### pub struct GasValidationResult + +/// Result of gas validation + +```rust +pub struct GasValidationResult +``` + +### pub fn print_report + +```rust +pub fn print_report(&self) +``` + +## utility_contracts\src\gas_metrics_examples.rs + +### pub struct PerformanceBaseline + +/// Utility structure for tracking performance across test runs + +```rust +pub struct PerformanceBaseline +``` + +### pub fn new + +```rust +pub fn new() -> Self +``` + +### pub fn add_baseline + +```rust +pub fn add_baseline(&mut self, operation: String, gas_cost: i128) +``` + +### pub fn check_regression + +```rust +pub fn check_regression(&self, max_regression_percent: f64) -> Vec +``` + +## utility_contracts\src\gas_metrics_integration.rs + +### pub fn measure_create_stream_operation + +/// Template for measuring create_continuous_stream + +```rust +pub fn measure_create_stream_operation( + stream_id: u64, + flow_rate: i128, + balance: i128, + label: &str, + ) +``` + +### pub fn measure_get_continuous_flow + +/// Template for measuring get_continuous_flow + +```rust +pub fn measure_get_continuous_flow(stream_id: u64, label: &str) +``` + +### pub fn measure_withdraw_continuous + +/// Template for measuring withdraw_continuous + +```rust +pub fn measure_withdraw_continuous(stream_id: u64, amount: i128, label: &str) +``` + +### pub fn measure_register_meter + +/// Template for measuring register_meter + +```rust +pub fn measure_register_meter(meter_id: u64, label: &str) +``` + +### pub fn measure_top_up + +/// Template for measuring top_up + +```rust +pub fn measure_top_up(meter_id: u64, amount: i128, label: &str) +``` + +### pub fn measure_claim_earnings + +/// Template for measuring claim_earnings + +```rust +pub fn measure_claim_earnings(meter_id: u64, label: &str) +``` + +### pub fn measure_update_heartbeat + +/// Template for measuring update_heartbeat + +```rust +pub fn measure_update_heartbeat(meter_id: u64, label: &str) +``` + +### pub fn measure_batch_register + +/// Template for measuring batch_register_meters + +```rust +pub fn measure_batch_register(num_meters: usize, label: &str) +``` + +### pub fn measure_batch_top_up + +/// Template for measuring batch_top_up + +```rust +pub fn measure_batch_top_up(num_meters: usize, label: &str) +``` + +### pub fn measure_batch_claim + +/// Template for measuring batch_claim + +```rust +pub fn measure_batch_claim(num_meters: usize, label: &str) +``` + +### pub fn measure_balance_calculation + +/// Measure balance calculation operation + +```rust +pub fn measure_balance_calculation(deposited: i128, streamed: i128, label: &str) +``` + +### pub fn measure_fee_calculation + +/// Measure fee calculation operation + +```rust +pub fn measure_fee_calculation(gross_amount: i128, fee_bps: i128, label: &str) +``` + +### pub fn measure_conservation_check + +/// Measure conservation invariant check + +```rust +pub fn measure_conservation_check(label: &str) +``` + +### pub fn measure_withdrawal + +/// Measure withdrawal operation + +```rust +pub fn measure_withdrawal(balance: i128, amount_withdrawn: i128, label: &str) +``` + +### pub fn measure_property_test_operation + +/// Measure gas for a property test iteration + +```rust +pub fn measure_property_test_operation(operation_type: &str, iteration: usize, label: &str) +``` + +### pub fn measure_property_test_batch + +/// Measure gas for multiple property test iterations + +```rust +pub fn measure_property_test_batch(operation_type: &str, mut num_iterations: usize) +``` + +### pub fn example_stream_lifecycle_with_gas_tracking + +/// Complete example showing gas metering for a full stream lifecycle + +```rust +pub fn example_stream_lifecycle_with_gas_tracking() +``` + +### pub fn example_batch_operations_with_gas_tracking + +/// Example showing gas tracking for batch operations + +```rust +pub fn example_batch_operations_with_gas_tracking() +``` + +### pub fn example_contract_constraint_validation + +/// Example showing constraint validation for contract operations + +```rust +pub fn example_contract_constraint_validation() +``` + +### pub fn example_gas_regression_detection + +/// Example showing gas regression detection + +```rust +pub fn example_gas_regression_detection() +``` + +## utility_contracts\src\ghost_sweeper.rs + +### pub struct GhostStreamPruned + +```rust +pub struct GhostStreamPruned +``` + +### pub struct StreamArchive + +```rust +pub struct StreamArchive +``` + +### pub enum PruneReason + +```rust +pub enum PruneReason +``` + +### pub struct GhostStreamCandidate + +```rust +pub struct GhostStreamCandidate +``` + +### pub struct SweeperResult + +```rust +pub struct SweeperResult +``` + +### pub struct GhostSweeper + +```rust +pub struct GhostSweeper +``` + +### pub fn prune_ghost_stream + +/// Prune a single ghost stream that has been zero balance for over 90 days + /// + /// # Arguments + /// * `env` - The contract environment + /// * `stream_id` - ID of the stream to prune + /// * `relayer` - Address of the relayer performing the cleanup + /// + /// # Returns + /// Gas bounty paid to the relayer + /// + /// # Errors + /// * `ContractError::MeterNotFound` - if stream doesn't exist + /// * `ContractError::StreamNotEligibleForPruning` - if stream not eligible + /// * `ContractError::StreamHasPendingBuffer` - if stream has pending buffer + +```rust +pub fn prune_ghost_stream(env: Env, stream_id: u64, relayer: Address) -> i128 +``` + +### pub fn batch_prune_ghost_streams + +/// Batch prune multiple ghost streams + /// + /// # Arguments + /// * `env` - The contract environment + /// * `stream_ids` - Vector of stream IDs to prune + /// * `relayer` - Address of the relayer performing the cleanup + /// + /// # Returns + /// Summary of the sweeping operation + +```rust +pub fn batch_prune_ghost_streams( + env: Env, + stream_ids: Vec, + relayer: Address, + ) -> SweeperResult +``` + +### pub fn get_ghost_stream_candidates + +/// Get list of ghost stream candidates + /// + /// # Arguments + /// * `env` - The contract environment + /// * `limit` - Maximum number of candidates to return + /// + /// # Returns + /// Vector of ghost stream candidates + +```rust +pub fn get_ghost_stream_candidates(env: Env, limit: u32) -> Vec +``` + +### pub fn check_stream_eligibility + +/// Check if a stream is eligible for pruning + /// + /// # Arguments + /// * `env` - The contract environment + /// * `stream_id` - Stream ID to check + /// + /// # Returns + /// Ghost stream candidate if eligible, None otherwise + +```rust +pub fn check_stream_eligibility(env: Env, stream_id: u64) -> Option +``` + +### pub fn get_stream_archive + +/// Get stream archive information + /// + /// # Arguments + /// * `env` - The contract environment + /// * `stream_id` - Stream ID + /// + /// # Returns + /// Stream archive if exists, None otherwise + +```rust +pub fn get_stream_archive(env: Env, stream_id: u64) -> Option +``` + +### pub fn get_sweeper_statistics + +/// Get global sweeper statistics + /// + /// # Arguments + /// * `env` - The contract environment + /// + /// # Returns + /// Sweeper statistics + +```rust +pub fn get_sweeper_statistics(env: Env) -> SweeperStatistics +``` + +### pub struct SweeperStatistics + +```rust +pub struct SweeperStatistics +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub enum ContractError + +```rust +pub enum ContractError +``` + +## utility_contracts\src\grant_stream_listener.rs + +### pub struct GrantMatch + +```rust +pub struct GrantMatch +``` + +### pub struct GrantConfig + +```rust +pub struct GrantConfig +``` + +### pub enum GrantDataKey + +```rust +pub enum GrantDataKey +``` + +### pub enum GrantError + +```rust +pub enum GrantError +``` + +### pub struct GrantStreamListener + +```rust +pub struct GrantStreamListener +``` + +### pub fn initialize + +/// Initialize the grant stream listener + +```rust +pub fn initialize(env: Env, admin: Address, treasury: Address) +``` + +### pub fn on_goal_reached + +/// Called by Utility-Protocol when a conservation goal is reached + +```rust +pub fn on_goal_reached( + env: Env, + billing_contract: Address, + goal_event: super::GoalReachedEvent, + ) +``` + +### pub fn get_grant_match + +/// Get grant match details + +```rust +pub fn get_grant_match(env: Env, goal_id: u64) -> GrantMatch +``` + +### pub fn get_provider_grants + +/// Get all grant matches for a provider + +```rust +pub fn get_provider_grants(env: Env, provider: Address) -> Vec +``` + +### pub fn get_grant_config + +/// Get grant configuration + +```rust +pub fn get_grant_config(env: Env) -> GrantConfig +``` + +### pub fn update_grant_config + +/// Update grant configuration (admin only) + +```rust +pub fn update_grant_config(env: Env, enabled: bool, max_grant_per_month: i128) +``` + +### pub fn update_treasury + +/// Update treasury address (admin only) + +```rust +pub fn update_treasury(env: Env, new_treasury: Address) +``` + +### pub fn get_provider_total_grants + +/// Get total grants awarded to a provider + +```rust +pub fn get_provider_total_grants(env: Env, provider: Address) -> i128 +``` + +### pub fn get_grant_statistics + +/// Get grant statistics + +```rust +pub fn get_grant_statistics(env: Env) -> (u64, i128, i128) +``` + +## utility_contracts\src\insurance_pool.rs + +### pub struct InsurancePoolMember + +```rust +pub struct InsurancePoolMember +``` + +### pub struct InsurancePool + +```rust +pub struct InsurancePool +``` + +### pub struct GovernanceProposal + +```rust +pub struct GovernanceProposal +``` + +### pub enum ProposalType + +```rust +pub enum ProposalType +``` + +### pub struct InsuranceClaim + +```rust +pub struct InsuranceClaim +``` + +### pub struct RiskAssessment + +```rust +pub struct RiskAssessment +``` + +### pub fn calculate_voting_power + +```rust +pub fn calculate_voting_power(&self, now: u64) -> i128 +``` + +### pub fn calculate_premium_multiplier + +```rust +pub fn calculate_premium_multiplier(&self) -> i128 +``` + +### pub enum InsuranceDataKey + +```rust +pub enum InsuranceDataKey +``` + +### pub fn get_insurance_pool + +```rust +pub fn get_insurance_pool(env: &Env) -> Result +``` + +### pub fn get_pool_member + +```rust +pub fn get_pool_member(env: &Env, user: &Address) -> Result +``` + +### pub fn calculate_risk_score + +```rust +pub fn calculate_risk_score(env: &Env, user: &Address, meter_id: u64) -> u32 +``` + +### pub fn calculate_premium_amount + +```rust +pub fn calculate_premium_amount( + env: &Env, + user: &Address, + meter_id: u64 +) -> Result +``` + +### pub fn create_insurance_pool + +```rust +pub fn create_insurance_pool( + env: &Env, + governance_admin: Address, + base_premium_rate_bps: i128, +) -> Result<(), ContractError> +``` + +### pub fn join_insurance_pool + +```rust +pub fn join_insurance_pool( + env: &Env, + user: Address, + meter_id: u64, + premium_amount: i128, +) -> Result<(), ContractError> +``` + +### pub fn submit_insurance_claim + +```rust +pub fn submit_insurance_claim( + env: &Env, + claimant: Address, + meter_id: u64, + requested_amount: i128, + reason: Symbol, +) -> Result +``` + +### pub fn process_approved_claim + +```rust +pub fn process_approved_claim(env: &Env, claim_id: u64) -> Result<(), ContractError> +``` + +### pub fn create_governance_proposal + +```rust +pub fn create_governance_proposal( + env: &Env, + proposer: Address, + proposal_type: ProposalType, + description: Symbol, + new_value: i128, +) -> Result +``` + +### pub fn vote_on_proposal + +```rust +pub fn vote_on_proposal( + env: &Env, + voter: Address, + proposal_id: u64, + vote_for: bool, +) -> Result<(), ContractError> +``` + +### pub fn execute_proposal + +```rust +pub fn execute_proposal(env: &Env, proposal_id: u64) -> Result<(), ContractError> +``` + +### pub fn allocate_claim_fees_to_pool + +```rust +pub fn allocate_claim_fees_to_pool(env: &Env, claim_amount: i128) -> i128 +``` + +## utility_contracts\src\lib.rs + +### pub struct PriceData + +```rust +pub struct PriceData +``` + +### pub enum ReputationTier + +```rust +pub enum ReputationTier +``` + +### pub struct ReputationScore + +```rust +pub struct ReputationScore +``` + +### pub enum IoTErrorCode + +```rust +pub enum IoTErrorCode +``` + +### pub fn from_contract_error + +/// Map a `ContractError` to the compact IoT u16 code. + +```rust +pub fn from_contract_error(e: ContractError) -> Self +``` + +### pub fn code + +/// Return the raw u16 code — zero-copy for firmware parsing. + +```rust +pub fn code(self) -> u16 +``` + +### pub struct ClawbackReconciliationExecuted + +```rust +pub struct ClawbackReconciliationExecuted +``` + +### pub struct GuarantorDeposit + +```rust +pub struct GuarantorDeposit +``` + +### pub struct CreditLimitApproached + +```rust +pub struct CreditLimitApproached +``` + +### pub struct ReadingRejected + +```rust +pub struct ReadingRejected +``` + +### pub struct AuditRecord + +```rust +pub struct AuditRecord +``` + +### pub struct GuarantorSlashed + +```rust +pub struct GuarantorSlashed +``` + +### pub enum BillingType + +```rust +pub enum BillingType +``` + +### pub enum StreamStatus + +```rust +pub enum StreamStatus +``` + +### pub struct ContinuousFlow + +```rust +pub struct ContinuousFlow +``` + +### pub struct UsageData + +```rust +pub struct UsageData +``` + +### pub struct UsageReport + +```rust +pub struct UsageReport +``` + +### pub enum ResourceType + +```rust +pub enum ResourceType +``` + +### pub struct SignedUsageData + +```rust +pub struct SignedUsageData +``` + +### pub struct SavingGoal + +```rust +pub struct SavingGoal +``` + +### pub struct Meter + +```rust +pub struct Meter +``` + +### pub struct SLAConfig + +```rust +pub struct SLAConfig +``` + +### pub struct SLAState + +```rust +pub struct SLAState +``` + +### pub struct SLADowntimeReport + +```rust +pub struct SLADowntimeReport +``` + +### pub struct SignedSLAReport + +```rust +pub struct SignedSLAReport +``` + +### pub struct ClaimSettlement + +```rust +pub struct ClaimSettlement +``` + +### pub struct DeliveryFailure + +```rust +pub struct DeliveryFailure +``` + +### pub struct PendingSettlement + +```rust +pub struct PendingSettlement +``` + +### pub struct ResellerConfig + +```rust +pub struct ResellerConfig +``` + +### pub struct ImpactMetrics + +```rust +pub struct ImpactMetrics +``` + +### pub struct ConservationGoal + +```rust +pub struct ConservationGoal +``` + +### pub struct OfflineReconciliation + +```rust +pub struct OfflineReconciliation +``` + +### pub struct GoalReachedEvent + +```rust +pub struct GoalReachedEvent +``` + +### pub struct Groth16Proof + +```rust +pub struct Groth16Proof +``` + +### pub struct Groth16VerificationKey + +```rust +pub struct Groth16VerificationKey +``` + +### pub struct ZKProof + +```rust +pub struct ZKProof +``` + +### pub struct ZKUsageReport + +```rust +pub struct ZKUsageReport +``` + +### pub struct EncryptedSensitivePayload + +```rust +pub struct EncryptedSensitivePayload +``` + +### pub struct SensitivePayloadAccepted + +```rust +pub struct SensitivePayloadAccepted +``` + +### pub struct TaxReceipt + +```rust +pub struct TaxReceipt +``` + +### pub struct PrivateBillingStatus + +```rust +pub struct PrivateBillingStatus +``` + +### pub struct CommitmentBatch + +```rust +pub struct CommitmentBatch +``` + +### pub struct MeterStatus + +```rust +pub struct MeterStatus +``` + +### pub struct MultiSigConfig + +```rust +pub struct MultiSigConfig +``` + +### pub struct WithdrawalRequest + +```rust +pub struct WithdrawalRequest +``` + +### pub struct FeeChangeProposal + +```rust +pub struct FeeChangeProposal +``` + +### pub struct GasBuffer + +```rust +pub struct GasBuffer +``` + +### pub struct FirmwareUpdateStartedEvent + +```rust +pub struct FirmwareUpdateStartedEvent +``` + +### pub struct FirmwareUpdateFinishedEvent + +```rust +pub struct FirmwareUpdateFinishedEvent +``` + +### pub struct UpdateCompleteData + +```rust +pub struct UpdateCompleteData +``` + +### pub struct SignedUpdateComplete + +```rust +pub struct SignedUpdateComplete +``` + +### pub struct ProviderWithdrawalWindow + +```rust +pub struct ProviderWithdrawalWindow +``` + +### pub struct DustAggregation + +```rust +pub struct DustAggregation +``` + +### pub struct DustCollectedEvent + +```rust +pub struct DustCollectedEvent +``` + +### pub struct StreamUpdatedEvent + +```rust +pub struct StreamUpdatedEvent +``` + +### pub struct BufferDepletedEvent + +```rust +pub struct BufferDepletedEvent +``` + +### pub struct BufferWarningEvent + +```rust +pub struct BufferWarningEvent +``` + +### pub struct StreamingFeeAccrued + +```rust +pub struct StreamingFeeAccrued +``` + +### pub struct UpgradeProposal + +```rust +pub struct UpgradeProposal +``` + +### pub struct AdminTransferProposal + +```rust +pub struct AdminTransferProposal +``` + +### pub struct UpgradeMultiSigConfig + +```rust +pub struct UpgradeMultiSigConfig +``` + +### pub enum UpgradeProposalStatus + +```rust +pub enum UpgradeProposalStatus +``` + +### pub struct UpgradeProposalV2 + +```rust +pub struct UpgradeProposalV2 +``` + +### pub struct LegalFreeze + +```rust +pub struct LegalFreeze +``` + +### pub enum VerificationMethod + +```rust +pub enum VerificationMethod +``` + +### pub struct VerifiedProvider + +```rust +pub struct VerifiedProvider +``` + +### pub struct SubDaoConfig + +```rust +pub struct SubDaoConfig +``` + +### pub struct WebhookConfig + +```rust +pub struct WebhookConfig +``` + +### pub struct LowBalanceAlert + +```rust +pub struct LowBalanceAlert +``` + +### pub struct BillingGroup + +```rust +pub struct BillingGroup +``` + +### pub struct MaintenanceMilestone + +```rust +pub struct MaintenanceMilestone +``` + +### pub struct ILProtectionBuffer + +```rust +pub struct ILProtectionBuffer +``` + +### pub struct TreasuryState + +```rust +pub struct TreasuryState +``` + +### pub struct TreasuryReconciliationEvent + +```rust +pub struct TreasuryReconciliationEvent +``` + +### pub struct SlaReportKey + +```rust +pub struct SlaReportKey +``` + +### pub struct CarbonCreditIssuedEvent + +```rust +pub struct CarbonCreditIssuedEvent +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub fn encode + +```rust +pub fn encode(&self, env: &Env) -> Bytes +``` + +### pub fn encode_raw_key + +/// Encode a raw key (e.g. u64) with a namespace prefix for domain separation. + +```rust +pub fn encode_raw_key(env: &Env, prefix: &[u8 +``` + +### pub fn migrate_namespace + +/// Migrate storage entries from legacy (non-prefixed) keys to new namespaced keys. +/// Handles tariff oracle keys and common singleton keys. Idempotent. + +```rust +pub fn migrate_namespace(env: &Env) +``` + +### pub enum ContractError + +```rust +pub enum ContractError +``` + +### pub struct PairingChallengeData + +```rust +pub struct PairingChallengeData +``` + +### pub struct RateLimitData + +```rust +pub struct RateLimitData +``` + +### pub struct EmergencyDrainRecord + +```rust +pub struct EmergencyDrainRecord +``` + +### pub struct MigrationCheckpoint + +```rust +pub struct MigrationCheckpoint +``` + +### pub struct MigrationRollback + +```rust +pub struct MigrationRollback +``` + +### pub struct UtilityContract + +```rust +pub struct UtilityContract +``` + +### pub fn assign_reseller + +/// Assigns a reseller to a specific meter with a defined fee percentage. + /// + /// # Arguments + /// * `env` - The execution environment. + /// * `meter_id` - The unique identifier of the meter. + /// * `reseller` - The address of the reseller to assign. + /// * `fee_bps` - The reseller fee in basis points (1 bp = 0.01%). + /// + /// # Panics + /// * Panics if the caller is not the provider of the meter. + /// * Panics if the meter does not exist (`ContractError::MeterNotFound`). + /// * Panics if `fee_bps` exceeds `MAX_RESELLER_FEE_BPS` (`ContractError::InvalidResellerFee`). + +```rust +pub fn assign_reseller(env: Env, meter_id: u64, reseller: Address, fee_bps: i128) +``` + +### pub fn claim_impact_sbt + +/// Claims an Impact Soulbound Token (SBT) for a user based on renewable energy usage. + /// + /// # Arguments + /// * `env` - The execution environment. + /// * `meter_id` - The unique identifier of the meter. + /// + /// # Panics + /// * Panics if the caller is not the user of the meter. + /// * Panics if the SBT has already been minted for this meter (`ContractError::SBTAlreadyMinted`). + /// * Panics if the renewable energy usage is below the threshold (`ContractError::ImpactNotSignificantEnough`). + +```rust +pub fn claim_impact_sbt(env: Env, meter_id: u64) +``` + +### pub fn get_minimum_balance_to_flow + +/// Retrieves the minimum balance required for a continuous flow to remain active. + /// + /// # Returns + /// * `i128` - The minimum balance required to flow. + +```rust +pub fn get_minimum_balance_to_flow() -> i128 +``` + +### pub fn set_oracle + +/// Sets the oracle contract address for price data. + /// + /// @dev This function is critical for contract operations as it determines + /// the source of all price data used for billing and conversions. + /// Only authorized administrators should be able to change this setting. + /// + /// @param env The Soroban execution environment + /// @param oracle_address The address of the oracle contract to set + /// + /// @notice Emits OracleSet event + /// @notice Reverts if caller is not authorized admin + /// + /// # Security Considerations + /// - Oracle address should be verified before setting + /// - Changing oracle address mid-operation could affect billing calculations + /// - Consider implementing a timelock for critical changes + /// + /// # Panics + /// * Panics if the caller is not the authorized admin (`ContractError::UnauthorizedAdmin`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let oracle_address = Address::from_string(&env, "CB..."); + /// UtilityContract::set_oracle(env, oracle_address); + /// ``` + +```rust +pub fn set_oracle(env: Env, oracle_address: Address) +``` + +### pub fn set_maintenance_config + +/// Sets the maintenance wallet address and protocol fee configuration. + /// + /// @dev Configures the wallet that receives protocol fees and the fee rate. + /// This is a critical administrative function that affects the economics + /// of the entire system. Only authorized administrators should be able to + /// modify these parameters. + /// + /// @param env The Soroban execution environment + /// @param wallet The address of the maintenance wallet to receive protocol fees + /// @param fee_bps The protocol fee in basis points (100 = 1%) + /// + /// @notice Emits MaintenanceConfigUpdated event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if fee_bps is negative or exceeds maximum allowed + /// + /// # Security Considerations + /// - Maintenance wallet should be a multi-sig or time-locked contract + /// - Fee changes should be announced in advance to users + /// - Consider implementing maximum fee limits + /// - High fees could discourage usage and affect adoption + /// + /// # Panics + /// * Panics if the caller is not the authorized admin (`ContractError::UnauthorizedAdmin`) + /// * Panics if fee_bps is negative (`ContractError::InvalidFeeAmount`) + /// * Panics if fee_bps exceeds MAX_PROTOCOL_FEE_BPS (`ContractError::ExcessiveFee`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let wallet = Address::from_string(&env, "GB..."); + /// let fee_bps = 50; // 0.5% + /// UtilityContract::set_maintenance_config(env, wallet, fee_bps); + /// ``` + +```rust +pub fn set_maintenance_config(env: Env, wallet: Address, fee_bps: i128) +``` + +### pub fn set_admin + +/// Sets the admin address for the contract, used for dust sweeper authorization. + /// + /// # Arguments + /// * `env` - The execution environment. + /// * `admin_address` - The address to be set as the new admin. + /// + /// # Panics + /// * Panics if the caller is not the current contract address (self-invocation). + +```rust +pub fn set_admin(env: Env, admin_address: Address) +``` + +### pub fn fund_gas_bounty + +/// Adds funds to the gas bounty pool used to reward dust sweepers. + /// + /// # Arguments + /// * `env` - The execution environment. + /// * `amount` - The amount of tokens to add to the gas bounty pool. + /// + /// # Panics + /// * Panics if the caller is not the authorized admin (`ContractError::UnauthorizedAdmin`). + /// * Panics if `amount` is zero or negative (`ContractError::InvalidTokenAmount`). + +```rust +pub fn fund_gas_bounty(env: Env, amount: i128) +``` + +### pub fn add_supported_token + +/// Marks a token address as supported by the system for payments and operations. + /// + /// @dev Enables a specific token for use within the utility payment system. + /// This is an administrative function that affects which tokens users + /// can use for bill payments and meter operations. Only authorized + /// administrators should be able to modify the supported token list. + /// + /// @param env The Soroban execution environment + /// @param token The token address to whitelist and enable + /// + /// @notice Emits TokenSupported event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if token address is invalid + /// + /// # Security Considerations + /// - Token contracts should be verified before being supported + /// - Consider implementing token metadata validation + /// - Malicious tokens could cause system disruptions + /// - Monitor for token depegging or contract issues + /// + /// # Panics + /// * Panics if the caller is not the authorized admin (`ContractError::UnauthorizedAdmin`) + /// * Panics if token address is zero address (`ContractError::InvalidAddress`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let token = Address::from_string(&env, "CD..."); + /// UtilityContract::add_supported_token(env, token); + /// ``` + +```rust +pub fn add_supported_token(env: Env, token: Address) +``` + +### pub fn remove_supported_token + +/// Removes a token from the system's supported token whitelist. + /// + /// @dev Disables a specific token from being used for new payments and operations. + /// This is an administrative function that should be used carefully as it + /// affects user ability to pay bills. Existing operations with the token + /// may continue until completion. Only authorized administrators should be + /// able to modify the supported token list. + /// + /// @param env The Soroban execution environment + /// @param token The token address to revoke and disable + /// + /// @notice Emits TokenUnsupported event + /// @notice Reverts if caller is not authorized admin + /// @notice Consider user impact when removing commonly used tokens + /// + /// # Security Considerations + /// - Provide advance notice before removing popular tokens + /// - Ensure users have alternative payment methods + /// - Consider implementing a gradual phase-out period + /// - Monitor for stranded user funds + /// + /// # Panics + /// * Panics if the caller is not the authorized admin (`ContractError::UnauthorizedAdmin`) + /// * Panics if token address is zero address (`ContractError::InvalidAddress`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let token = Address::from_string(&env, "CD..."); + /// UtilityContract::remove_supported_token(env, token); + /// ``` + +```rust +pub fn remove_supported_token(env: Env, token: Address) +``` + +### pub fn add_supported_withdraw_token + +/// Adds a withdrawal token to the supported list for path payments. + /// + /// @dev Enables a specific token for withdrawal operations and path payments. + /// This expands the options users have for receiving funds and making + /// cross-token payments. Only authorized administrators should be able + /// to modify the withdrawal token configuration. + /// + /// @param env The Soroban execution environment + /// @param token The token address to enable for withdrawals + /// + /// @notice Emits WithdrawTokenSupported event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if token address is invalid + /// + /// # Security Considerations + /// - Withdrawal tokens should have sufficient liquidity + /// - Verify token contracts before enabling + /// - Consider withdrawal fees and slippage + /// - Monitor for token stability issues + /// + /// # Panics + /// * Panics if the caller is not the authorized admin (`ContractError::UnauthorizedAdmin`) + /// * Panics if token address is zero address (`ContractError::InvalidAddress`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let token = Address::from_string(&env, "CD..."); + /// UtilityContract::add_supported_withdraw_token(env, token); + /// ``` + +```rust +pub fn add_supported_withdraw_token(env: Env, token: Address) +``` + +### pub fn remove_supported_withdraw_token + +/// Removes a withdrawal token from the supported list for path payments. + /// + /// @dev Disables a specific token for withdrawal operations and path payments. + /// This should be used carefully as it affects user options for receiving + /// funds. Only authorized administrators should be able to modify the + /// withdrawal token configuration. + /// + /// @param env The Soroban execution environment + /// @param token The token address to disable for withdrawals + /// + /// @notice Emits WithdrawTokenUnsupported event + /// @notice Reverts if caller is not authorized admin + /// @notice Consider user impact when removing withdrawal options + /// + /// # Security Considerations + /// - Provide advance notice before removing popular withdrawal tokens + /// - Ensure users have alternative withdrawal methods + /// - Monitor for stranded user funds + /// - Consider implementing a grace period + /// + /// # Panics + /// * Panics if the caller is not the authorized admin (`ContractError::UnauthorizedAdmin`) + /// * Panics if token address is zero address (`ContractError::InvalidAddress`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let token = Address::from_string(&env, "CD..."); + /// UtilityContract::remove_supported_withdraw_token(env, token); + /// ``` + +```rust +pub fn remove_supported_withdraw_token(env: Env, token: Address) +``` + +### pub fn emergency_drain + +/// Emergency drain mechanism for recovering stranded assets from the contract. + /// + /// @dev Critical emergency function to recover funds when normal operations + /// are compromised or funds become stranded. This function includes + /// multiple safety mechanisms including cooldown periods, amount limits, + /// and comprehensive audit trails. Only authorized administrators can + /// execute this function. + /// + /// @param env The Soroban execution environment + /// @param recipient The address to receive the drained funds + /// @param amount The amount of native tokens to drain (in stroops) + /// @param reason Human-readable reason for the emergency drain + /// + /// @notice Emits EmergencyDrainExecuted event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if cooldown period has not elapsed + /// @notice Reverts if amount is below minimum threshold + /// @notice Reverts if insufficient contract balance + /// + /// # Security Considerations + /// - 24-hour cooldown prevents abuse and allows for oversight + /// - Minimum amount threshold prevents spam drains + /// - Comprehensive audit trail for all drain operations + /// - Recipient validation prevents funds from being sent to invalid addresses + /// - Balance checks ensure contract can maintain operational reserves + /// - Consider implementing multi-sig requirement for additional security + /// + /// # Panics + /// * Panics if caller is not authorized admin (`ContractError::EmergencyDrainNotAuthorized`) + /// * Panics if cooldown period not elapsed (`ContractError::EmergencyDrainCooldownActive`) + /// * Panics if amount below minimum (`ContractError::InvalidTokenAmount`) + /// * Panics if insufficient balance (`ContractError::EmergencyDrainInsufficientBalance`) + /// * Panics if recipient address is invalid (`ContractError::InvalidAddress`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let recipient = Address::from_string(&env, "GB..."); + /// let amount = 10_000_000; // 0.001 XLM + /// let reason = String::from_str(&env, "Critical security incident recovery"); + /// UtilityContract::emergency_drain(env, recipient, amount, reason); + /// ``` + +```rust +pub fn emergency_drain(env: Env, recipient: Address, amount: i128, reason: String) +``` + +### pub fn get_last_emergency_drain + +/// Get the last emergency drain execution timestamp. + /// + /// @dev Returns the timestamp of the last emergency drain execution. + /// Useful for checking cooldown status and monitoring. + /// + /// @param env The Soroban execution environment + /// + /// @return Option - Timestamp of last execution, or None if never executed + /// + /// # Examples + /// ```rust + /// let last_drain = UtilityContract::get_last_emergency_drain(env); + /// if let Some(timestamp) = last_drain { + /// // Check if cooldown has elapsed + /// } + /// ``` + +```rust +pub fn get_last_emergency_drain(env: Env) -> Option +``` + +### pub fn get_emergency_drain_record + +/// Get emergency drain record by ID. + /// + /// @dev Returns detailed information about a specific emergency drain. + /// Useful for audit purposes and transparency. + /// + /// @param env The Soroban execution environment + /// @param drain_id The ID of the emergency drain record + /// + /// @return Option - Drain record if found, None otherwise + /// + /// # Examples + /// ```rust + /// let record = UtilityContract::get_emergency_drain_record(env, 1); + /// if let Some(drain) = record { + /// // Process drain record + /// } + /// ``` + +```rust +pub fn get_emergency_drain_record(env: Env, drain_id: u64) -> Option +``` + +### pub fn get_emergency_drain_count + +/// Get total count of emergency drain executions. + /// + /// @dev Returns the total number of emergency drains executed. + /// Useful for monitoring and audit purposes. + /// + /// @param env The Soroban execution environment + /// + /// @return u64 - Total count of emergency drain executions + /// + /// # Examples + /// ```rust + /// let count = UtilityContract::get_emergency_drain_count(env); + /// ``` + +```rust +pub fn get_emergency_drain_count(env: Env) -> u64 +``` + +### pub fn create_conservation_goal + +/// Create a new conservation goal for a provider + +```rust +pub fn create_conservation_goal( + env: Env, + provider: Address, + target_water_savings: i128, + deadline: u64, + grant_amount: i128, + grant_token: Address, + ) -> u64 +``` + +### pub fn update_water_savings + +/// Update water savings for a conservation goal + +```rust +pub fn update_water_savings(env: Env, goal_id: u64, additional_savings: i128) +``` + +### pub fn configure_grant_stream_match + +/// Configure Grant Stream contract to listen for goal achievements + +```rust +pub fn configure_grant_stream_match(env: Env, goal_id: u64, grant_stream_contract: Address) +``` + +### pub fn get_conservation_goal + +/// Get conservation goal details + +```rust +pub fn get_conservation_goal(env: Env, goal_id: u64) -> ConservationGoal +``` + +### pub fn get_provider_conservation_goals + +/// Get all active conservation goals for a provider + +```rust +pub fn get_provider_conservation_goals(env: Env, provider: Address) -> Vec +``` + +### pub fn check_and_trigger_grant + +/// Check if a goal has been achieved and trigger grant if needed + +```rust +pub fn check_and_trigger_grant(env: Env, goal_id: u64) +``` + +### pub fn set_green_energy_discount + +/// Set green energy discount for a specific meter (in basis points) + +```rust +pub fn set_green_energy_discount(env: Env, meter_id: u64, discount_bps: i128) +``` + +### pub fn set_velocity_limit_config + +/// Configure velocity limit parameters for the utility payment system. + /// + /// @dev Sets global and per-stream velocity limits to prevent excessive outflows + /// and protect against rapid fund depletion. This is a critical administrative + /// function that affects system-wide security and user experience. Only authorized + /// administrators should be able to modify these parameters. + /// + /// @param env The Soroban execution environment + /// @param admin Admin address that must authorize this change + /// @param global_limit Maximum system-wide outflow per 24 hours (in stroops) + /// @param per_stream_limit Maximum per-meter outflow per 24 hours (in stroops) + /// @param is_enabled Whether velocity limiting is active + /// + /// @notice Emits VelocityConfigUpdated event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if limits are invalid or inconsistent + /// + /// # Security Considerations + /// - Global limit should be set based on system capacity and risk tolerance + /// - Per-stream limit prevents individual meters from draining the system + /// - Consider seasonal variations in usage patterns + /// - Monitor for velocity limit breaches and adjust as needed + /// - Emergency overrides should be available for critical situations + /// + /// # Panics + /// * Panics if the caller is not the authorized admin + /// * Panics if global_limit or per_stream_limit are <= 0 (`ContractError::InvalidTokenAmount`) + /// * Panics if per_stream_limit > global_limit (`ContractError::VelocityLimitBreach`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let admin = Address::from_string(&env, "GB..."); + /// let global_limit = 100_000_000_000; // 1000 XLM per day + /// let per_stream_limit = 10_000_000_000; // 100 XLM per meter per day + /// UtilityContract::set_velocity_limit_config(env, admin, global_limit, per_stream_limit, true); + /// ``` + +```rust +pub fn set_velocity_limit_config( + env: Env, + admin: Address, + global_limit: i128, + per_stream_limit: i128, + is_enabled: bool, + ) +``` + +### pub fn apply_velocity_override + +/// Apply temporary override to suspend velocity limits for specific or global operations. + /// + /// @dev Allows authorized administrators to temporarily bypass velocity limits + /// for emergency situations, maintenance, or false positive resolution. + /// This is a powerful administrative function that should be used sparingly + /// and with proper justification. All overrides are tracked with expiration + /// times and audit trails. + /// + /// @param env The Soroban execution environment + /// @param admin Admin multi-sig address that must authorize this change + /// @param meter_id Meter to override (0 for global override affecting all meters) + /// @param expires_at Unix timestamp when override expires (0 = never expires) + /// @param reason Reason code for audit trail (e.g., "false_positive", "maintenance") + /// + /// @notice Emits VelocityOverrideApplied event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if meter_id is invalid + /// + /// # Security Considerations + /// - Overrides should be time-limited whenever possible + /// - Global overrides affect all meters and should be used with extreme caution + /// - All overrides create audit trails for compliance review + /// - Consider implementing multi-sig requirement for override operations + /// - Monitor override usage patterns for potential abuse + /// + /// # Panics + /// * Panics if the caller is not the authorized admin + /// * Panics if meter_id is invalid (doesn't exist) + /// * Panics if expires_at is in the past + /// + /// # Examples + /// ```rust + /// use soroban_sdk::{Address, Symbol}; + /// let admin = Address::from_string(&env, "GB..."); + /// let meter_id = 123; + /// let expires_at = env.ledger().timestamp() + 3600; // 1 hour from now + /// let reason = symbol_short!("maintenance"); + /// UtilityContract::apply_velocity_override(env, admin, meter_id, expires_at, reason); + /// ``` + +```rust +pub fn apply_velocity_override( + env: Env, + admin: Address, + meter_id: u64, + expires_at: u64, + reason: Symbol, + ) +``` + +### pub fn revoke_velocity_override + +/// Revoke an active velocity override and restore normal velocity limiting. + /// + /// @dev Removes a previously applied velocity override, restoring normal + /// velocity limit enforcement. This is an administrative function that + /// should be used when overrides are no longer needed or were applied + /// in error. Only authorized administrators can revoke overrides. + /// + /// @param env The Soroban execution environment + /// @param admin Admin address that must authorize this change + /// @param meter_id Meter override to revoke (0 for global override) + /// + /// @notice Emits VelocityOverrideRevoked event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if no active override exists for the specified meter + /// + /// # Security Considerations + /// - Verify that revoking the override won't cause immediate limit breaches + /// - Consider providing advance notice before revoking critical overrides + /// - Monitor system behavior after override revocation + /// - Document the reason for revocation in audit logs + /// + /// # Panics + /// * Panics if the caller is not the authorized admin + /// * Panics if no active override exists for the specified meter + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let admin = Address::from_string(&env, "GB..."); + /// let meter_id = 123; // Revoke override for specific meter + /// UtilityContract::revoke_velocity_override(env, admin, meter_id); + /// ``` + +```rust +pub fn revoke_velocity_override(env: Env, admin: Address, meter_id: u64) +``` + +### pub fn get_velocity_limits + +/// Get current velocity limit configuration + +```rust +pub fn get_velocity_limits(env: Env) -> Option +``` + +### pub fn add_sla_node + +/// Register a trusted monitoring node for SLA (Service Level Agreement) reporting. + /// + /// @dev Adds a new trusted node that can submit downtime reports and SLA + /// measurements. This is a critical administrative function that affects + /// the reliability of SLA monitoring and penalty calculations. Only + /// authorized administrators should be able to modify the trusted node set. + /// + /// @param env The Soroban execution environment + /// @param admin Admin address that must authorize this change + /// @param node_pk The 32-byte public key of the monitoring node + /// + /// @notice Emits SLANodeRegistered event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if node_pk is invalid + /// + /// # Security Considerations + /// - Node public keys should be verified and authenticated off-chain + /// - Consider implementing node reputation and monitoring systems + /// - Regular audits of trusted nodes should be conducted + /// - Compromised nodes should be removed immediately + /// - Consider implementing node rotation policies + /// + /// # Panics + /// * Panics if the caller is not the authorized admin + /// * Panics if node_pk is invalid (wrong length or format) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::{Address, BytesN}; + /// let admin = Address::from_string(&env, "GB..."); + /// let node_pk = BytesN::from_array(&env, &[0u8; 32]); + /// UtilityContract::add_sla_node(env, admin, node_pk); + /// ``` + +```rust +pub fn add_sla_node(env: Env, admin: Address, node_pk: BytesN<32>) +``` + +### pub fn remove_sla_node + +/// Remove a trusted monitoring node from the SLA reporting system. + /// + /// @dev Removes a node's trusted status, preventing it from submitting + /// further downtime reports. This is an administrative function that + /// should be used when nodes are compromised, decommissioned, or + /// no longer trusted. Only authorized administrators can remove nodes. + /// + /// @param env The Soroban execution environment + /// @param admin Admin address that must authorize this change + /// @param node_pk The 32-byte public key of the monitoring node to remove + /// + /// @notice Emits SLANodeRemoved event + /// @notice Reverts if caller is not authorized admin + /// @notice Reverts if node_pk is invalid + /// + /// # Security Considerations + /// - Immediate removal of compromised nodes is critical + /// - Consider implementing a grace period for non-critical removals + /// - Document the reason for node removal in audit logs + /// - Monitor system behavior after node removal + /// - Consider implementing node rotation to maintain system health + /// + /// # Panics + /// * Panics if the caller is not the authorized admin + /// * Panics if node_pk is invalid (wrong length or format) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::{Address, BytesN}; + /// let admin = Address::from_string(&env, "GB..."); + /// let node_pk = BytesN::from_array(&env, &[0u8; 32]); + /// UtilityContract::remove_sla_node(env, admin, node_pk); + /// ``` + +```rust +pub fn remove_sla_node(env: Env, admin: Address, node_pk: BytesN<32>) +``` + +### pub fn set_sla_config + +/// Configure SLA parameters for a specific meter's service level monitoring. + /// + /// @dev Sets Service Level Agreement parameters including uptime thresholds + /// and penalty multipliers for a specific meter. This affects how downtime + /// is calculated and penalties are applied. Only the meter's provider can + /// modify these parameters for their own meters. + /// + /// @param env The Soroban execution environment + /// @param meter_id The unique identifier of the meter + /// @param config SLA configuration including thresholds and penalties + /// + /// @notice Emits SLAConfigUpdated event + /// @notice Reverts if caller is not the meter provider + /// @notice Reverts if meter does not exist + /// @notice Reverts if config parameters are invalid + /// + /// # Security Considerations + /// - Penalty multipliers should be reasonable and proportional + /// - Thresholds should reflect realistic service expectations + /// - Consider regulatory requirements for SLA parameters + /// - Monitor SLA compliance rates and adjust as needed + /// - Document SLA terms clearly for users + /// + /// # Panics + /// * Panics if the caller is not the meter provider + /// * Panics if the meter does not exist (`ContractError::MeterNotFound`) + /// * Panics if config parameters are invalid (`ContractError::InvalidUsageValue`) + /// + /// # Examples + /// ```rust + /// use soroban_sdk::Address; + /// let config = SLAConfig { + /// threshold_seconds: 3600, // 1 hour uptime requirement + /// penalty_multiplier_bps: 500, // 5% penalty multiplier + /// }; + /// UtilityContract::set_sla_config(env, 123, config); + /// ``` + +```rust +pub fn set_sla_config(env: Env, meter_id: u64, config: SLAConfig) +``` + +### pub fn submit_sla_report + +/// Submit a signed downtime report from a trusted monitoring node. + /// + /// @dev Allows trusted monitoring nodes to submit signed downtime reports + /// for SLA monitoring. Reports are processed using a consensus mechanism + /// where multiple nodes must submit similar reports before they are + /// accepted. This prevents false reports and ensures data reliability. + /// + /// @param env The Soroban execution environment + /// @param signed_report The signed SLA report containing downtime data + /// + /// @notice Emits SLAReportSubmitted event + /// @notice Reverts if node is not trusted + /// @notice Reverts if signature is invalid + /// @notice Silently ignores duplicate reports from the same node + /// + /// # Security Considerations + /// - Only trusted nodes can submit reports + /// - All reports must be cryptographically signed + /// - Consensus mechanism prevents single-node manipulation + /// - Duplicate reports are rejected to prevent spam + /// - Temporary storage ensures reports don't persist indefinitely + /// - Consider implementing report validation and anomaly detection + /// + /// # Panics + /// * Panics if the node is not trusted (`ContractError::NodeNotTrusted`) + /// * Panics if the signature is invalid (`ContractError::InvalidSignature`) + /// * Panics if the meter does not exist (`ContractError::MeterNotFound`) + /// + /// # Examples + /// ```rust + /// let report = SLAReport { + /// meter_id: 123, + /// start_time: 1640995200, // Jan 1, 2022 + /// end_time: 1640998800, // Jan 1, 2022 + 1 hour + /// }; + /// let signature = sign_report(&node_private_key, &report); + /// let signed_report = SignedSLAReport { + /// report, + /// node_public_key: node_public_key, + /// signature, + /// }; + /// UtilityContract::submit_sla_report(env, signed_report); + /// ``` + +```rust +pub fn submit_sla_report(env: Env, signed_report: SignedSLAReport) +``` + +### pub fn register_meter + +```rust +pub fn register_meter( + env: Env, + user: Address, + provider: Address, + off_peak_rate: i128, + token: Address, + device_public_key: BytesN<32>, + priority_index: u32, + resource_type: ResourceType, + ) -> u64 +``` + +### pub fn register_with_referral + +```rust +pub fn register_with_referral( + env: Env, + user: Address, + provider: Address, + off_peak_rate: i128, + token: Address, + device_public_key: BytesN<32>, + referrer: Address, + priority_index: u32, + resource_type: ResourceType, + ) -> u64 +``` + +### pub fn register_device + +/// Register a device MAC address hash and bind it to a meter (streaming channel) + /// The MAC address is stored as a SHA-256 hash for privacy + /// Returns the meter ID if successful + +```rust +pub fn register_device( + env: Env, + meter_id: u64, + mac_address: BytesN<32>, // Expects SHA-256 hash of MAC address (32 bytes) + owner: Address, // Owner of the device (must authenticate) + ) -> u64 +``` + +### pub fn initiate_device_transfer + +/// Initiate device reassignment with mutual consent requirement + /// Current owner initiates transfer to new owner + /// Returns a transfer ID that must be confirmed by new owner + +```rust +pub fn initiate_device_transfer( + env: Env, + meter_id: u64, + new_owner: Address, + current_owner: Address, + ) -> BytesN<32> +``` + +### pub fn complete_device_transfer + +/// Complete device reassignment with mutual consent + /// New owner confirms the transfer that was initiated by current owner + /// After confirmation, device is bound to new owner's meter + +```rust +pub fn complete_device_transfer( + env: Env, + meter_id: u64, + new_owner: Address, + transfer_id: BytesN<32>, + ) -> u64 +``` + +### pub fn register_meter_with_mode + +/// Register a device MAC address hash and bind it to a meter (streaming channel) + /// The MAC address is stored as a SHA-256 hash for privacy + /// Returns the meter ID if successful + +```rust +pub fn register_meter_with_mode( + env: Env, + user: Address, + provider: Address, + off_peak_rate: i128, + token: Address, + billing_type: BillingType, + device_public_key: BytesN<32>, + priority_index: u32, + resource_type: ResourceType, + ) -> u64 +``` + +### pub fn top_up + +```rust +pub fn top_up(env: Env, meter_id: u64, amount: i128, contributor: Address) +``` + +### pub fn initiate_pairing + +```rust +pub fn initiate_pairing(env: Env, meter_id: u64) -> BytesN<32> +``` + +### pub fn complete_pairing + +```rust +pub fn complete_pairing(env: Env, meter_id: u64, signature: BytesN<64>) +``` + +### pub fn ping + +```rust +pub fn ping(env: Env, meter_id: u64) +``` + +### pub fn deduct_units + +```rust +pub fn deduct_units(env: Env, signed_data: SignedUsageData) +``` + +### pub fn get_audit_head + +/// Return the current audit-chain head for off-chain monitors. + +```rust +pub fn get_audit_head(env: Env) -> Option +``` + +### pub fn get_audit_record + +/// Return an audit record by sequence number for verification or dashboard indexing. + +```rust +pub fn get_audit_record(env: Env, sequence: u64) -> Option +``` + +### pub fn verify_audit_chain + +/// Verify the audit hash chain between two inclusive sequence numbers. + +```rust +pub fn verify_audit_chain(env: Env, start_sequence: u64, end_sequence: u64) -> bool +``` + +### pub fn claim + +```rust +pub fn claim(env: Env, meter_id: u64) +``` + +### pub fn update_usage + +```rust +pub fn update_usage(env: Env, meter_id: u64, watt_hours_consumed: i128) +``` + +### pub fn reset_cycle_usage + +```rust +pub fn reset_cycle_usage(env: Env, meter_id: u64) +``` + +### pub fn get_usage_data + +```rust +pub fn get_usage_data(env: Env, meter_id: u64) -> Option +``` + +### pub fn get_meter + +```rust +pub fn get_meter(env: Env, meter_id: u64) -> Option +``` + +### pub fn get_count + +```rust +pub fn get_count(env: Env) -> u64 +``` + +### pub fn get_provider_window + +```rust +pub fn get_provider_window(env: Env, provider: Address) -> Option +``` + +### pub fn get_provider_total_pool + +```rust +pub fn get_provider_total_pool(env: Env, provider: Address) -> i128 +``` + +### pub fn get_watt_hours_display + +```rust +pub fn get_watt_hours_display(precise_watt_hours: i128, precision_factor: i128) -> i128 +``` + +### pub fn calculate_expected_depletion + +```rust +pub fn calculate_expected_depletion(env: Env, meter_id: u64) -> Option +``` + +### pub fn set_meter_pause + +```rust +pub fn set_meter_pause(env: Env, meter_id: u64, paused: bool) +``` + +### pub fn set_tiered_pricing + +```rust +pub fn set_tiered_pricing(env: Env, meter_id: u64, threshold: i128, rate: i128) +``` + +### pub fn vote_for_asset + +```rust +pub fn vote_for_asset(env: Env, voter: Address, asset_symbol: Symbol) +``` + +### pub fn get_votes + +```rust +pub fn get_votes(env: Env, asset_symbol: Symbol) -> i128 +``` + +### pub fn emergency_shutdown + +```rust +pub fn emergency_shutdown(env: Env, meter_id: u64) +``` + +### pub fn set_max_flow_rate + +```rust +pub fn set_max_flow_rate(env: Env, meter_id: u64, max_rate_per_hour: i128) +``` + +### pub fn update_heartbeat + +```rust +pub fn update_heartbeat(env: Env, meter_id: u64) +``` + +### pub fn withdraw_earnings + +```rust +pub fn withdraw_earnings(env: Env, meter_id: u64, amount_usd_cents: i128) +``` + +### pub fn get_current_rate + +```rust +pub fn get_current_rate(env: Env) -> Option +``` + +### pub fn is_meter_offline + +```rust +pub fn is_meter_offline(env: Env, meter_id: u64) -> bool +``` + +### pub fn transfer_meter_ownership + +/// Unlink a meter from its current tenant and link it to a new tenant. + /// All historical usage data is preserved. Requires auth from the current + /// user, the new user, and the provider. + +```rust +pub fn transfer_meter_ownership(env: Env, meter_id: u64, new_user: Address) +``` + +### pub fn update_continuous_flow_rate + +/// Create a new continuous flow stream + /// Update the flow rate of an existing continuous stream + +```rust +pub fn update_continuous_flow_rate(env: Env, stream_id: u64, new_flow_rate: i128) +``` + +### pub fn add_continuous_balance + +/// Add balance to a continuous flow stream + +```rust +pub fn add_continuous_balance(env: Env, stream_id: u64, additional_balance: i128) +``` + +### pub fn get_continuous_flow + +/// Get the current state of a continuous flow stream + +```rust +pub fn get_continuous_flow(env: Env, stream_id: u64) -> Option +``` + +### pub fn calculate_continuous_depletion + +/// Calculate expected depletion time for a continuous flow stream + +```rust +pub fn calculate_continuous_depletion(env: Env, stream_id: u64) -> Option +``` + +### pub fn pause_continuous_flow + +/// Pause a continuous flow stream + +```rust +pub fn pause_continuous_flow(env: Env, stream_id: u64) +``` + +### pub fn resume_continuous_flow + +/// Resume a continuous flow stream with specified rate + +```rust +pub fn resume_continuous_flow(env: Env, stream_id: u64, flow_rate_per_second: i128) +``` + +### pub fn initiate_firmware_update + +/// Initiate a firmware update for a meter (provider-only) + /// This pauses billing during the update window and requires device signature to resume + +```rust +pub fn initiate_firmware_update(env: Env, meter_id: u64) +``` + +### pub fn complete_firmware_update + +/// Complete firmware update with device signature + /// Device must sign the UpdateCompleteData to resume billing + +```rust +pub fn complete_firmware_update(env: Env, signed_update: SignedUpdateComplete) +``` + +### pub fn get_billing_group + +```rust +pub fn get_billing_group(env: Env, parent_account: Address) -> Option +``` + +### pub fn remove_meter_from_billing_group + +```rust +pub fn remove_meter_from_billing_group(env: Env, parent_account: Address, meter_id: u64) +``` + +### pub fn estimate_meter_monthly_cost + +```rust +pub fn estimate_meter_monthly_cost( + env: Env, + is_group_meter: bool, + _meters_in_group: u32, + ) -> i128 +``` + +### pub fn get_operation_cost + +```rust +pub fn get_operation_cost(_env: Env, operation: String) -> i128 +``` + +### pub fn configure_webhook + +```rust +pub fn configure_webhook(env: Env, user: Address, webhook_url: String) +``` + +### pub fn deactivate_webhook + +```rust +pub fn deactivate_webhook(env: Env, user: Address) +``` + +### pub fn get_webhook_config + +```rust +pub fn get_webhook_config(env: Env, user: Address) -> Option +``` + +### pub fn get_pending_alerts + +```rust +pub fn get_pending_alerts(env: Env, user: Address) -> Vec +``` + +### pub fn claim_with_alerts + +```rust +pub fn claim_with_alerts(env: Env, meter_id: u64) +``` + +### pub fn add_authorized_contributor + +```rust +pub fn add_authorized_contributor(env: Env, meter_id: u64, contributor: Address) +``` + +### pub fn remove_authorized_contributor + +```rust +pub fn remove_authorized_contributor(env: Env, meter_id: u64, contributor: Address) +``` + +### pub fn get_contribution + +```rust +pub fn get_contribution(env: Env, meter_id: u64, contributor: Address) -> i128 +``` + +### pub fn challenge_service + +```rust +pub fn challenge_service(env: Env, meter_id: u64) +``` + +### pub fn resolve_challenge + +```rust +pub fn resolve_challenge(env: Env, meter_id: u64, restored: bool) +``` + +### pub fn refund_disputed_funds + +```rust +pub fn refund_disputed_funds(env: Env, meter_id: u64) +``` + +### pub fn set_credit_drip + +```rust +pub fn set_credit_drip(env: Env, meter_id: u64, drip_rate: i128) +``` + +### pub fn set_carbon_credit_config + +/// Configure carbon credit asset and drip rate for a meter. + /// Provider must authorize this update. + +```rust +pub fn set_carbon_credit_config(env: Env, meter_id: u64, token: Address, drip_rate_bps: i128) +``` + +### pub fn set_priority_index + +```rust +pub fn set_priority_index(env: Env, meter_id: u64, priority_index: u32) +``` + +### pub fn apply_throttling_if_needed + +```rust +pub fn apply_throttling_if_needed(env: Env, meter_id: u64) +``` + +### pub fn set_government_vault + +```rust +pub fn set_government_vault(env: Env, vault_address: Address) +``` + +### pub fn set_tax_rate + +```rust +pub fn set_tax_rate(env: Env, tax_rate_bps: i128) +``` + +### pub fn get_maintenance_fund + +```rust +pub fn get_maintenance_fund(env: Env, meter_id: u64) -> i128 +``` + +### pub fn manual_extend_ttl + +```rust +pub fn manual_extend_ttl(env: Env, meter_id: u64) +``` + +### pub fn propose_upgrade + +```rust +pub fn propose_upgrade(env: Env, new_wasm_hash: BytesN<32>) +``` + +### pub fn submit_upgrade_veto + +```rust +pub fn submit_upgrade_veto(env: Env, proposal_id: u64) +``` + +### pub fn finalize_upgrade + +```rust +pub fn finalize_upgrade(env: Env) +``` + +### pub fn get_storage_version_public + +/// Get the current storage version + +```rust +pub fn get_storage_version_public(env: Env) -> u32 +``` + +### pub fn finalize_upgrade_v2 + +/// Finalize upgrade with storage version checking + /// This is the enhanced version that validates storage compatibility + +```rust +pub fn finalize_upgrade_v2(env: Env, new_storage_version: u32) +``` + +### pub fn run_migration + +/// Run migration for storage version upgrade + /// This function can be called multiple times to complete a migration in batches + /// Returns true if migration is complete, false if more calls are needed + +```rust +pub fn run_migration(env: Env, target_version: u32) -> bool +``` + +### pub fn rollback_migration + +/// Roll back the current storage version to the previous compatible version. + +```rust +pub fn rollback_migration(env: Env, target_version: u32) -> bool +``` + +### pub fn get_migration_checkpoint + +/// Return the active migration checkpoint for monitoring dashboards. + +```rust +pub fn get_migration_checkpoint(env: Env) -> Option +``` + +### pub fn get_migration_rollback + +/// Return rollback metadata for the supplied source version. + +```rust +pub fn get_migration_rollback(env: Env, from_version: u32) -> Option +``` + +### pub fn cancel_migration + +/// Cancel an ongoing migration (admin only) + /// This is useful if a migration encounters issues and needs to be reset + +```rust +pub fn cancel_migration(env: Env) +``` + +### pub fn is_migration_active + +/// Check if a migration is currently in progress + +```rust +pub fn is_migration_active(env: Env) -> bool +``` + +### pub fn initiate_admin_transfer + +/// Initialize admin transfer with 48-hour timelock + /// During the window, active users can veto (requires 10% to succeed) + +```rust +pub fn initiate_admin_transfer(env: Env, proposed_admin: Address) +``` + +### pub fn veto_admin_transfer + +/// Submit veto against admin transfer + /// Requires 10% of active users to veto + +```rust +pub fn veto_admin_transfer(env: Env, user: Address) +``` + +### pub fn execute_admin_transfer + +/// Execute admin transfer after 48-hour timelock if not vetoed + +```rust +pub fn execute_admin_transfer(env: Env) +``` + +### pub fn set_initial_admin + +/// Set current admin (initialization only) + +```rust +pub fn set_initial_admin(env: Env, admin: Address) +``` + +### pub fn register_active_user + +/// Register as active user (for governance tracking) + +```rust +pub fn register_active_user(env: Env, user: Address) +``` + +### pub fn legal_freeze + +/// Initiate legal freeze on a meter (compliance officer only) + +```rust +pub fn legal_freeze(env: Env, meter_id: u64, reason: String) +``` + +### pub fn release_legal_freeze + +/// Release legal freeze (requires compliance council multi-sig) + +```rust +pub fn release_legal_freeze(env: Env, meter_id: u64, council_signatures: Vec
) +``` + +### pub fn set_compliance_officer + +/// Set compliance officer address + +```rust +pub fn set_compliance_officer(env: Env, officer: Address) +``` + +### pub fn set_legal_vault + +/// Set legal vault address + +```rust +pub fn set_legal_vault(env: Env, vault: Address) +``` + +### pub fn get_legal_freeze + +/// Get legal freeze info + +```rust +pub fn get_legal_freeze(env: Env, meter_id: u64) -> LegalFreeze +``` + +### pub fn request_provider_verification + +/// Request provider verification + +```rust +pub fn request_provider_verification(env: Env, provider_name: String) +``` + +### pub fn grant_provider_verification + +/// Grant verification to provider (admin or community vote) + +```rust +pub fn grant_provider_verification(env: Env, provider: Address, method: VerificationMethod) +``` + +### pub fn is_provider_verified + +/// Check if provider is verified + +```rust +pub fn is_provider_verified(env: Env, provider: Address) -> bool +``` + +### pub fn get_provider_info + +/// Get provider info + +```rust +pub fn get_provider_info(env: Env, provider: Address) -> VerifiedProvider +``` + +### pub fn create_sub_dao + +/// Create Sub-DAO configuration + +```rust +pub fn create_sub_dao(env: Env, sub_dao: Address, allocated_budget: i128, token: Address) +``` + +### pub fn create_sub_dao_stream + +/// Create stream from Sub-DAO (uses allocated budget) + +```rust +pub fn create_sub_dao_stream( + env: Env, + user: Address, + provider: Address, + off_peak_rate: i128, + token: Address, + device_public_key: BytesN<32>, + priority_index: u32, + resource_type: ResourceType, + ) -> u64 +``` + +### pub fn recall_sub_dao_funds + +/// Recall funds from Sub-DAO (parent DAO only) + +```rust +pub fn recall_sub_dao_funds(env: Env, sub_dao: Address, amount: i128) +``` + +### pub fn deactivate_sub_dao + +/// Deactivate Sub-DAO + +```rust +pub fn deactivate_sub_dao(env: Env, sub_dao: Address) +``` + +### pub fn get_sub_dao_config + +/// Get Sub-DAO config + +```rust +pub fn get_sub_dao_config(env: Env, sub_dao: Address) -> SubDaoConfig +``` + +### pub fn configure_multisig_withdrawal + +/// Configure multi-sig withdrawal requirement for a provider. + /// This sets up the Finance Department wallets that can authorize large withdrawals. + /// + /// # Arguments + /// * `provider` - The utility provider address + /// * `finance_wallets` - Vector of authorized Finance Department wallet addresses (3-5 wallets) + /// * `required_signatures` - Number of signatures required (must be <= wallet count) + /// * `threshold_amount` - Minimum amount in USD cents requiring multi-sig approval + +```rust +pub fn configure_multisig_withdrawal( + env: Env, + provider: Address, + finance_wallets: Vec
, + required_signatures: u32, + threshold_amount: i128, + ) +``` + +### pub fn update_multisig_config + +/// Update multi-sig configuration for a provider. + /// Requires provider authorization and enforces distinct signer and threshold bounds. + +```rust +pub fn update_multisig_config( + env: Env, + provider: Address, + new_finance_wallets: Vec
, + new_required_signatures: u32, + new_threshold_amount: i128, + ) +``` + +### pub fn propose_multisig_withdrawal + +/// Propose a multi-sig withdrawal request. + /// Only authorized Finance Department wallets can propose withdrawals. + /// + /// # Arguments + /// * `provider` - The utility provider address + /// * `meter_id` - The meter to withdraw earnings from + /// * `amount_usd_cents` - Amount to withdraw in USD cents + /// * `destination` - Treasury address to receive funds + /// + /// # Returns + /// The request ID for this withdrawal proposal + +```rust +pub fn propose_multisig_withdrawal( + env: Env, + provider: Address, + meter_id: u64, + amount_usd_cents: i128, + destination: Address, + ) -> u64 +``` + +### pub fn approve_multisig_withdrawal + +/// Approve a pending multi-sig withdrawal request. + /// Only authorized Finance Department wallets can approve. + /// + /// # Arguments + /// * `provider` - The utility provider address + /// * `request_id` - The withdrawal request ID to approve + +```rust +pub fn approve_multisig_withdrawal( + env: Env, + provider: Address, + request_id: u64, + approver: Address, + ) +``` + +### pub fn execute_multisig_withdrawal + +/// Execute a multi-sig withdrawal after sufficient approvals. + /// + /// # Arguments + /// * `provider` - The utility provider address + /// * `request_id` - The withdrawal request ID to execute + +```rust +pub fn execute_multisig_withdrawal(env: Env, provider: Address, request_id: u64) +``` + +### pub fn revoke_multisig_approval + +/// Revoke a previously given approval for a withdrawal request. + +```rust +pub fn revoke_multisig_approval(env: Env, provider: Address, request_id: u64) +``` + +### pub fn cancel_multisig_withdrawal + +/// Cancel a pending multi-sig withdrawal request. + +```rust +pub fn cancel_multisig_withdrawal(env: Env, provider: Address, request_id: u64) +``` + +### pub fn disable_multisig + +/// Disable multi-sig requirement for a provider. + +```rust +pub fn disable_multisig(env: Env, provider: Address) +``` + +### pub fn get_withdrawal_request_count + +```rust +pub fn get_withdrawal_request_count(env: Env, provider: Address) -> u64 +``` + +### pub fn set_meter_encryption_key + +/// Register the active off-chain encryption key identifier for a meter. + /// + /// The key material itself is never stored on-chain. `key_id` should be a + /// SHA-256 fingerprint of the recipient public key or KMS key version used + /// by the meter/provider E2EE channel. + +```rust +pub fn set_meter_encryption_key(env: Env, meter_id: u64, key_id: BytesN<32>) +``` + +### pub fn submit_sensitive_payload + +/// Store an end-to-end encrypted sensitive payload envelope. + /// + /// The contract validates metadata, ciphertext bounds, key version, and the + /// commitment over the envelope, but never receives plaintext or decryption + /// keys. This keeps sensitive usage/location/diagnostic fields confidential + /// while preserving auditability and replay-resistant indexing. + +```rust +pub fn submit_sensitive_payload(env: Env, payload: EncryptedSensitivePayload) +``` + +### pub fn get_sensitive_payload + +```rust +pub fn get_sensitive_payload( + env: Env, + meter_id: u64, + commitment: BytesN<32>, + ) -> Option +``` + +### pub fn enable_privacy_mode + +```rust +pub fn enable_privacy_mode(env: Env, meter_id: u64) +``` + +### pub fn disable_privacy_mode + +/// Disable privacy mode for a meter + +```rust +pub fn disable_privacy_mode(env: Env, meter_id: u64) +``` + +### pub fn create_continuous_stream + +/// Create a new continuous flow stream with mandatory buffer deposit + /// Buffer must equal at least 24 hours of the negotiated flow rate + +```rust +pub fn create_continuous_stream( + env: Env, + stream_id: u64, + flow_rate_per_second: i128, + initial_balance: i128, + provider: Address, + payer: Address, + priority_tier: u32, + device_mac_pubkey: BytesN<32>, + ) +``` + +### pub fn set_zk_verification_key + +```rust +pub fn set_zk_verification_key(env: Env, meter_id: u64, vk: Groth16VerificationKey) +``` + +### pub fn submit_zk_usage_report + +```rust +pub fn submit_zk_usage_report( + env: Env, + meter_id: u64, + proof: Groth16Proof, + public_inputs: Vec, + nullifier: BytesN<32>, + ) +``` + +### pub fn add_continuous_buffer + +```rust +pub fn add_continuous_buffer(env: Env, stream_id: u64, additional_buffer: i128) +``` + +### pub fn close_stream_amicably + +```rust +pub fn close_stream_amicably(env: Env, stream_id: u64) -> i128 +``` + +### pub fn withdraw_continuous + +/// Withdraw from a continuous flow stream + +```rust +pub fn withdraw_continuous(env: Env, stream_id: u64, withdrawal_amount: i128) -> i128 +``` + +### pub fn get_required_buffer + +```rust +pub fn get_required_buffer(_env: Env, flow_rate_per_second: i128) -> i128 +``` + +### pub fn get_buffer_balance + +```rust +pub fn get_buffer_balance(env: Env, stream_id: u64) -> Option +``` + +### pub fn get_private_billing_status + +```rust +pub fn get_private_billing_status(env: Env, meter_id: u64) -> PrivateBillingStatus +``` + +### pub fn sweep_dust + +```rust +pub fn sweep_dust( + env: Env, + caller: Address, + token_address: Address, + max_streams: Option, + ) -> DustCollectedEvent +``` + +### pub fn get_dust_aggregation + +```rust +pub fn get_dust_aggregation(env: Env, token_address: Address) -> Option +``` + +### pub fn has_dust + +```rust +pub fn has_dust(env: Env, stream_id: u64) -> bool +``` + +### pub fn initialize_gas_buffer + +```rust +pub fn initialize_gas_buffer( + env: Env, + provider: Address, + token: Address, + initial_amount: i128, + ) +``` + +### pub fn top_up_gas_buffer + +```rust +pub fn top_up_gas_buffer(env: Env, provider: Address, token: Address, amount: i128) +``` + +### pub fn withdraw_from_gas_buffer + +```rust +pub fn withdraw_from_gas_buffer(env: Env, provider: Address, token: Address, amount: i128) +``` + +### pub fn get_gas_buffer + +```rust +pub fn get_gas_buffer(env: Env, provider: Address) -> Option +``` + +### pub fn get_gas_buffer_balance + +```rust +pub fn get_gas_buffer_balance(env: Env, provider: Address) -> i128 +``` + +### pub fn set_platform_fee_bps + +/// Set the platform streaming fee in basis points (admin only). + /// E.g. 50 bps = 0.5%. Max is 1000 bps (10%). + +```rust +pub fn set_platform_fee_bps(env: Env, fee_bps: i128) +``` + +### pub fn set_protocol_fee_vault + +/// Set the Protocol Fee Vault address (admin only). + /// Only authorized DAO multi-sigs should be set here. + +```rust +pub fn set_protocol_fee_vault(env: Env, vault: Address) +``` + +### pub fn collect_streaming_fees + +/// Sweep accrued streaming fees for a stream to the Protocol Fee Vault. + /// Anyone can call this; the vault address is set by the admin. + +```rust +pub fn collect_streaming_fees(env: Env, stream_id: u64) -> i128 +``` + +### pub fn get_platform_fee_bps + +/// Get the current platform fee in basis points. + +```rust +pub fn get_platform_fee_bps(env: Env) -> i128 +``` + +### pub fn get_accrued_streaming_fees + +/// Get accrued streaming fees for a stream (not yet swept to vault). + +```rust +pub fn get_accrued_streaming_fees(env: Env, stream_id: u64) -> i128 +``` + +### pub fn set_min_route_threshold + +/// Set the minimum capital threshold for yield routing (admin only). + /// route_to_yield will abort if available capital is below this value. + +```rust +pub fn set_min_route_threshold(env: Env, threshold: i128) +``` + +### pub fn get_min_route_threshold + +/// Get the current minimum yield-routing threshold. + +```rust +pub fn get_min_route_threshold(env: Env) -> i128 +``` + +### pub fn route_to_yield + +/// Route capital to yield-generating DeFi protocols. + /// Aborts if `amount` is below the configured MIN_ROUTE_THRESHOLD to avoid + /// spending more in gas than the yield would earn. + /// + /// Issue #280: Implements fallback error handling for failed cross-contract calls. + /// Returns the amount actually routed (may be less than requested if fallback occurs). + +```rust +pub fn route_to_yield(env: Env, amount: i128) -> i128 +``` + +### pub fn claim_pending + +```rust +pub fn claim_pending(env: Env, user: Address, batch_id: BytesN<32>) +``` + +### pub fn set_provider_fleet_cap + +```rust +pub fn set_provider_fleet_cap(env: Env, provider: Address, new_cap: i128, authority: Address) +``` + +### pub fn set_dao_governor + +```rust +pub fn set_dao_governor(env: Env, dao: Address) +``` + +### pub fn set_grid_administrator + +```rust +pub fn set_grid_administrator(env: Env, grid_admin: Address) +``` + +### pub fn grid_shortage_load_shed + +```rust +pub fn grid_shortage_load_shed( + env: Env, + provider: Address, + min_surviving_tier: u32, + grid_admin: Address, + ) +``` + +### pub fn stream_device_heartbeat + +```rust +pub fn stream_device_heartbeat( + env: Env, + stream_id: u64, + meter_id: u64, + signature: BytesN<64>, + pub_key: BytesN<32>, + ) +``` + +### pub fn pardon_stream_liveness + +```rust +pub fn pardon_stream_liveness(env: Env, stream_id: u64) +``` + +### pub fn apply_liveness_slash + +```rust +pub fn apply_liveness_slash( + env: Env, + stream_id: u64, + meter_id: u64, + stale_threshold_ledgers: u32, + ) -> i128 +``` + +### pub fn p2p_finalize_exchange + +```rust +pub fn p2p_finalize_exchange( + env: Env, + supplier: Address, + consumer: Address, + utility_treasury: Address, + supply_rate: i128, + demand_rate: i128, + delta_seconds: i128, + grid_fee_bps: i128, + battery_credit_cap: i128, + token: Address, + ) -> (i128, i128) +``` + +### pub fn get_utility_reputation + +/// Read-only reputation query for partner DApps (e.g. lending vaults). + /// + /// Returns a `ReputationScore` derived from the user's on-chain liveness and + /// buffer health. Emits **zero events** and exposes **no consumption volume + /// or device MAC**. Defaults to a neutral `NewUser` score when history has + /// been pruned or the user is unknown. + /// + /// Designed for high-frequency cross-contract queries — all storage reads are + /// single-key lookups to minimise CPU instruction count. + +```rust +pub fn get_utility_reputation(env: Env, user: Address) -> ReputationScore +``` + +### pub fn refresh_reputation_cache + +/// Refresh and cache the reputation score for a user. + /// Call this after significant state changes to keep the cache warm. + +```rust +pub fn refresh_reputation_cache(env: Env, user: Address) +``` + +### pub fn get_iot_error_code + +/// Return the compact u16 IoT error code for a given `ContractError` variant. + /// Firmware devices call this to map on-chain errors to local recovery actions. + +```rust +pub fn get_iot_error_code(error_variant: u32) -> u32 +``` + +### pub fn sync_actual_balance + +/// Reconcile the contract's internal accounting with the actual on-chain + /// token balance after a Stellar Asset Contract (SAC) clawback event. + /// + /// # Security + /// - Only callable by the admin. + /// - Verifies that the actual balance is genuinely lower than tracked TVL + /// before applying any haircut (prevents fake-clawback attacks). + /// - If the clawback targets a specific user, only that user's streams are + /// terminated. + /// + /// Emits `ClawbackReconciliationExecuted`. + +```rust +pub fn sync_actual_balance( + env: Env, + token: Address, + expected_tvl: i128, + affected_user: Option
, + ) +``` + +### pub fn lock_guarantor_deposit + +/// Lock USDC collateral into a guarantor deposit vault. + /// The deposit backs one or more post-paid streams. + +```rust +pub fn lock_guarantor_deposit( + env: Env, + owner: Address, + collateral_token: Address, + amount: i128, + ) +``` + +### pub fn accrue_postpaid_debt + +/// Accrue post-paid debt against a guarantor deposit. + /// + /// Called internally by the provider when billing a post-paid stream. + /// Emits `CreditLimitApproached` at 80 % and slashes at 100 %. + +```rust +pub fn accrue_postpaid_debt(env: Env, owner: Address, debt_amount: i128) +``` + +### pub fn settle_postpaid_debt + +/// Settle post-paid debt manually (user pays off their bill). + +```rust +pub fn settle_postpaid_debt(env: Env, owner: Address, payment_amount: i128) +``` + +### pub fn get_guarantor_deposit + +/// Get the current guarantor deposit for a user. + +```rust +pub fn get_guarantor_deposit(env: Env, owner: Address) -> Option +``` + +## utility_contracts\src\lib_original.rs + +### pub struct PriceData + +```rust +pub struct PriceData +``` + +### pub enum BillingType + +```rust +pub enum BillingType +``` + +### pub struct UsageReport + +```rust +pub struct UsageReport +``` + +### pub struct SignedUsageData + +```rust +pub struct SignedUsageData +``` + +### pub struct UsageData + +```rust +pub struct UsageData +``` + +### pub struct Meter + +```rust +pub struct Meter +``` + +### pub struct ProviderWithdrawalWindow + +```rust +pub struct ProviderWithdrawalWindow +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +### pub enum ContractError + +```rust +pub enum ContractError +``` + +### pub struct PairingChallengeData + +```rust +pub struct PairingChallengeData +``` + +### pub struct UtilityContract + +```rust +pub struct UtilityContract +``` + +### pub fn get_minimum_balance_to_flow + +```rust +pub fn get_minimum_balance_to_flow() -> i128 +``` + +### pub fn set_oracle + +```rust +pub fn set_oracle(env: Env, oracle_address: Address) +``` + +### pub fn set_maintenance_config + +```rust +pub fn set_maintenance_config(env: Env, wallet: Address, fee_bps: i128) +``` + +### pub fn add_supported_token + +```rust +pub fn add_supported_token(env: Env, token: Address) +``` + +### pub fn remove_supported_token + +```rust +pub fn remove_supported_token(env: Env, token: Address) +``` + +### pub fn register_meter + +```rust +pub fn register_meter( + env: Env, + user: Address, + provider: Address, + off_peak_rate: i128, + token: Address, + device_public_key: BytesN<32>, + ) -> u64 +``` + +### pub fn register_meter_with_mode + +```rust +pub fn register_meter_with_mode( + env: Env, + user: Address, + provider: Address, + off_peak_rate: i128, + token: Address, + billing_type: BillingType, + device_public_key: BytesN<32>, + ) -> u64 +``` + +### pub fn top_up + +```rust +pub fn top_up(env: Env, meter_id: u64, amount: i128) +``` + +### pub fn top_up_with_token + +```rust +pub fn top_up_with_token(env: Env, meter_id: u64, amount: i128, payment_token: Address) +``` + +### pub fn initiate_pairing + +```rust +pub fn initiate_pairing(env: Env, meter_id: u64) -> BytesN<32> +``` + +### pub fn complete_pairing + +```rust +pub fn complete_pairing(env: Env, meter_id: u64, signature: BytesN<64>) +``` + +### pub fn update_device_public_key + +```rust +pub fn update_device_public_key(env: Env, meter_id: u64, new_public_key: BytesN<32>) +``` + +### pub fn deduct_units + +```rust +pub fn deduct_units(env: Env, signed_data: SignedUsageData) +``` + +### pub fn deduct_units + +```rust +pub fn deduct_units(env: Env, meter_id: u64, units_consumed: i128) +``` + +### pub fn claim + +```rust +pub fn claim(env: Env, meter_id: u64) +``` + +### pub fn update_usage + +```rust +pub fn update_usage(env: Env, meter_id: u64, watt_hours_consumed: i128) +``` + +### pub fn reset_cycle_usage + +```rust +pub fn reset_cycle_usage(env: Env, meter_id: u64) +``` + +### pub fn get_usage_data + +```rust +pub fn get_usage_data(env: Env, meter_id: u64) -> Option +``` + +### pub fn get_meter + +```rust +pub fn get_meter(env: Env, meter_id: u64) -> Option +``` + +### pub fn get_provider_window + +```rust +pub fn get_provider_window(env: Env, provider: Address) -> Option +``` + +### pub fn get_watt_hours_display + +```rust +pub fn get_watt_hours_display(precise_watt_hours: i128, precision_factor: i128) -> i128 +``` + +### pub fn calculate_expected_depletion + +```rust +pub fn calculate_expected_depletion(env: Env, meter_id: u64) -> Option +``` + +### pub fn set_max_flow_rate + +```rust +pub fn set_max_flow_rate(env: Env, meter_id: u64, max_rate_per_hour: i128) +``` + +### pub fn update_heartbeat + +```rust +pub fn update_heartbeat(env: Env, meter_id: u64) +``` + +### pub fn withdraw_earnings + +```rust +pub fn withdraw_earnings(env: Env, meter_id: u64, amount_usd_cents: i128) +``` + +### pub fn get_current_rate + +```rust +pub fn get_current_rate(env: Env) -> Option +``` + +### pub fn is_meter_offline + +```rust +pub fn is_meter_offline(env: Env, meter_id: u64) -> bool +``` + +### pub fn get_watt_hours_display + +```rust +pub fn get_watt_hours_display(watt_hours: i128, precision_factor: i128) -> i128 +``` + +### pub fn transfer_meter_ownership + +/// Unlink a meter from its current tenant and link it to a new tenant. + /// All historical usage data is preserved. Requires auth from the current + /// user, the new user, and the provider. + +```rust +pub fn transfer_meter_ownership(env: Env, meter_id: u64, new_user: Address) +``` + +## utility_contracts\src\multi_sensor.rs + +### pub struct MasterStream + +```rust +pub struct MasterStream +``` + +### pub fn add_sensor + +```rust +pub fn add_sensor(env: &Env, account: Address, mac: String) +``` + +### pub fn remove_sensor + +```rust +pub fn remove_sensor(env: &Env, account: Address, mac: String) +``` + +### pub fn record_consumption + +```rust +pub fn record_consumption(env: &Env, account: Address, mac: String, payload: i128) +``` + +### pub fn validate_invariants + +```rust +pub fn validate_invariants(env: &Env, account: Address) +``` + +## utility_contracts\src\Multi_Sig.rs + +### pub struct MasterStream + +```rust +pub struct MasterStream +``` + +### pub fn add_sensor + +```rust +pub fn add_sensor(env: &Env, account: Address, mac: String) +``` + +### pub fn remove_sensor + +```rust +pub fn remove_sensor(env: &Env, account: Address, mac: String) +``` + +### pub fn record_consumption + +```rust +pub fn record_consumption(env: &Env, account: Address, mac: String, payload: i128) +``` + +### pub fn validate_invariants + +```rust +pub fn validate_invariants(env: &Env, account: Address) +``` + +### pub struct GrantMatch + +```rust +pub struct GrantMatch +``` + +### pub struct GrantConfig + +```rust +pub struct GrantConfig +``` + +### pub enum GrantDataKey + +```rust +pub enum GrantDataKey +``` + +### pub enum GrantError + +```rust +pub enum GrantError +``` + +### pub struct GrantStreamListener + +```rust +pub struct GrantStreamListener +``` + +### pub fn initialize + +/// Initialize the grant stream listener + +```rust +pub fn initialize(env: Env, admin: Address, treasury: Address) +``` + +### pub fn on_goal_reached + +/// Called by Utility-Protocol when a conservation goal is reached + +```rust +pub fn on_goal_reached(env: Env, goal_event: super::GoalReachedEvent) +``` + +### pub fn get_grant_match + +/// Get grant match details + +```rust +pub fn get_grant_match(env: Env, goal_id: u64) -> GrantMatch +``` + +### pub fn get_provider_grants + +/// Get all grant matches for a provider + +```rust +pub fn get_provider_grants(env: Env, provider: Address) -> Vec +``` + +### pub fn get_grant_config + +/// Get grant configuration + +```rust +pub fn get_grant_config(env: Env) -> GrantConfig +``` + +### pub fn update_grant_config + +/// Update grant configuration (admin only) + +```rust +pub fn update_grant_config(env: Env, enabled: bool, max_grant_per_month: i128) +``` + +### pub fn update_treasury + +/// Update treasury address (admin only) + +```rust +pub fn update_treasury(env: Env, new_treasury: Address) +``` + +### pub fn get_provider_total_grants + +/// Get total grants awarded to a provider + +```rust +pub fn get_provider_total_grants(env: Env, provider: Address) -> i128 +``` + +### pub fn get_grant_statistics + +/// Get grant statistics + +```rust +pub fn get_grant_statistics(env: Env) -> (u64, i128, i128) +``` + +## utility_contracts\src\nonce_sync.rs + +### pub struct NonceDesyncAlert + +```rust +pub struct NonceDesyncAlert +``` + +### pub enum NonceAlertType + +```rust +pub enum NonceAlertType +``` + +### pub struct DeviceNonceState + +```rust +pub struct DeviceNonceState +``` + +### pub struct NonceResetRequest + +```rust +pub struct NonceResetRequest +``` + +### pub struct SignedHeartbeat + +```rust +pub struct SignedHeartbeat +``` + +### pub fn new + +/// Creates a new device nonce state with the specified initial nonce. + /// + /// This function initializes the nonce state for a new device or after + /// a complete reset. All counters start at zero and the device is not + /// marked as suspicious. + /// + /// # Arguments + /// + /// * `initial_nonce` - The initial nonce value for the device + /// + /// # Returns + /// + /// A new `DeviceNonceState` with initialized values + /// + /// # Security Considerations + /// + /// - Initial nonce should be cryptographically random + /// - Avoid using sequential initial nonces across devices + /// - Consider using device-specific seed values + +```rust +pub fn new(initial_nonce: u64) -> Self +``` + +### pub fn should_mark_suspicious + +/// Determines if the device should be marked as suspicious based on desync patterns. + /// + /// This function implements the suspicious device detection logic. + /// A device is marked suspicious if it has more than 10 desync events + /// within a 24-hour period, indicating potential compromise or + /// severe network issues. + /// + /// # Returns + /// + /// `true` if the device should be marked as suspicious, `false` otherwise + /// + /// # Security Threshold + /// + /// The threshold of 10 desyncs per 24 hours provides a balance between: + /// - Detecting actual security issues + /// - Avoiding false positives from poor network conditions + /// - Allowing for temporary network disruptions + +```rust +pub fn should_mark_suspicious(&self) -> bool +``` + +### pub fn update_desync_count + +/// Updates the desync counter, resetting if 24 hours have passed. + /// + /// This function maintains the rolling 24-hour count of desync events. + /// If more than 24 hours have passed since the last reset, the counter + /// is reset to zero and the reset timestamp is updated. + /// + /// # Arguments + /// + /// * `current_time` - Current Unix timestamp + /// + /// # Side Effects + /// + /// - Increments `desync_count_24h` + /// - Updates `desync_count_reset` if 24 hours have passed + /// - May update `is_suspicious` based on new count + +```rust +pub fn update_desync_count(&mut self, current_time: u64) +``` + +### pub struct NonceSyncManager + +```rust +pub struct NonceSyncManager +``` + +### pub fn verify_heartbeat_nonce + +/// Verifies a device heartbeat and updates the nonce state. + /// + /// This function is the core of the nonce synchronization system. It validates + /// the heartbeat signature, checks the nonce sequence, and updates the device + /// state if the heartbeat is valid. Invalid heartbeats trigger desync alerts. + /// + /// # Arguments + /// + /// * `env` - The contract environment + /// * `heartbeat` - Signed heartbeat payload containing nonce and signature + /// + /// # Returns + /// + /// `true` if the heartbeat nonce is valid and device state updated, + /// `false` if the nonce is invalid (desync detected) + /// + /// # Errors + /// + /// * `ContractError::InvalidSignature` - if signature verification fails + /// * `ContractError::PublicKeyMismatch` - if public key doesn't match device + /// + /// # Security Behavior + /// + /// - **Valid Heartbeat**: Updates nonce to `received_nonce + 1` + /// - **Invalid Nonce**: Emits `NonceDesyncAlert` event + /// - **Repeated Desyncs**: May mark device as suspicious + /// - **Signature Failure**: Contract panic with security error + /// + /// # Network Considerations + /// + /// Nonces within the window (+1 to +5) are accepted to handle UDP packet loss + /// and network reordering, but still emit desync alerts for monitoring. + +```rust +pub fn verify_heartbeat_nonce(env: Env, heartbeat: SignedHeartbeat) -> bool +``` + +### pub fn reset_device_nonce + +/// Resets a device nonce through multi-signature authorization. + /// + /// This function provides a secure mechanism to reset device nonces when + /// a device has been compromised, replaced, or requires synchronization + /// recovery. The reset requires multiple authorized signers to prevent + /// unauthorized nonce manipulation. + /// + /// # Arguments + /// + /// * `env` - The contract environment + /// * `meter_id` - Unique identifier of the utility meter + /// * `device_mac` - MAC address of the IoT device (32-byte hash) + /// * `new_nonce` - New nonce value to set for the device + /// * `reset_request` - Multi-signature reset request data + /// * `approver` - Address of the current approver + /// + /// # Errors + /// + /// * `ContractError::UnauthorizedDevice` - if approver not authorized + /// * `ContractError::InsufficientApprovals` - if not enough approvals + /// * `ContractError::AdminExecutionWindowExpired` - if request expired + /// * `ContractError::AlreadyApprovedWithdrawal` - if already approved + /// + /// # Security Process + /// + /// 1. Verify approver is in authorized resetters list + /// 2. Check request hasn't expired + /// 3. Verify approver hasn't already approved + /// 4. Add approver's signature to request + /// 5. If threshold reached: execute reset immediately + /// 6. Clear all security counters and suspicion flags + /// + /// # Multi-Sig Requirements + /// + /// - Default: 3-of-5 multi-signature scheme + /// - All signers must be pre-authorized + /// - Requests expire after 24 hours + /// - Execution requires threshold approvals + +```rust +pub fn reset_device_nonce( + env: Env, + meter_id: u64, + device_mac: BytesN<32>, + new_nonce: u64, + mut reset_request: NonceResetRequest, + approver: Address, + ) +``` + +### pub fn get_device_nonce_state + +/// Retrieves the current nonce state for a specific device. + /// + /// This function returns the complete nonce state for a device, + /// including the current expected nonce, last heartbeat time, + /// desync statistics, and suspicion status. + /// + /// # Arguments + /// + /// * `env` - The contract environment + /// * `device_mac` - MAC address of the IoT device (32-byte hash) + /// + /// # Returns + /// + /// Current device nonce state. If no state exists, returns a new + /// state with nonce 0 (useful for device initialization). + /// + /// # Security Monitoring + /// + /// The returned state can be used to: + /// - Check if device is marked as suspicious + /// - Monitor desync frequency + /// - Verify last heartbeat time + /// - Assess device health metrics + +```rust +pub fn get_device_nonce_state(env: Env, device_mac: BytesN<32>) -> DeviceNonceState +``` + +### pub fn is_device_suspicious + +/// Checks if a device is currently marked as suspicious. + /// + /// This function provides a quick check for suspicious device status, + /// which can be used by other contract functions to apply additional + /// security measures or restrictions to suspicious devices. + /// + /// # Arguments + /// + /// * `env` - The contract environment + /// * `device_mac` - MAC address of the IoT device (32-byte hash) + /// + /// # Returns + /// + /// `true` if the device is marked as suspicious, `false` otherwise + /// + /// # Security Implications + /// + /// Suspicious devices may: + /// - Be blocked from certain operations + /// - Require additional verification + /// - Trigger security alerts + /// - Be subject to manual review + +```rust +pub fn is_device_suspicious(env: Env, device_mac: BytesN<32>) -> bool +``` + +### pub fn initialize_device_nonce + +/// Initializes nonce tracking for a new device. + /// + /// This function sets up the nonce state for a new device or reinitializes + /// an existing device. It should be called when a device is first paired + /// or when a device is replaced and needs fresh nonce tracking. + /// + /// # Arguments + /// + /// * `env` - The contract environment + /// * `device_mac` - MAC address of the IoT device (32-byte hash) + /// * `initial_nonce` - Initial nonce value for the device + /// + /// # Security Considerations + /// + /// - Initial nonce should be cryptographically random + /// - Use device-specific seeds to prevent nonce collisions + /// - Consider using timestamp + device hash for uniqueness + /// - Document the nonce initialization process + /// + /// # Use Cases + /// + /// - New device onboarding + /// - Device replacement after compromise + /// - Firmware reset with new nonce sequence + /// - Recovery from extended network outages + +```rust +pub fn initialize_device_nonce(env: Env, device_mac: BytesN<32>, initial_nonce: u64) +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +## utility_contracts\src\oracle_flow.rs + +### pub struct OracleFlow + +```rust +pub struct OracleFlow +``` + +### pub fn new + +```rust +pub fn new(owner: AccountId, oracle: AccountId, initial_rate: u128, hard_cap: u128) -> Self +``` + +### pub fn update_rate + +```rust +pub fn update_rate(&mut self, new_rate: u128) -> Result<(), String> +``` + +### pub fn set_hard_cap + +```rust +pub fn set_hard_cap(&mut self, new_cap: u128) -> Result<(), String> +``` + +### pub fn get_rate + +```rust +pub fn get_rate(&self) -> u128 +``` + +### pub fn get_cap + +```rust +pub fn get_cap(&self) -> u128 +``` + +## utility_contracts\src\sbt_minter.rs + +### pub enum SBTError + +```rust +pub enum SBTError +``` + +### pub enum SBTDataKey + +```rust +pub enum SBTDataKey +``` + +### pub struct SBTMetadata + +```rust +pub struct SBTMetadata +``` + +### pub struct ImpactSBTMinter + +```rust +pub struct ImpactSBTMinter +``` + +### pub fn initialize + +/// Initialize the SBT contract with the authorized minter (the main Utility Contract) + +```rust +pub fn initialize(env: Env, admin: Address) +``` + +### pub fn mint_impact_sbt + +/// Mint the Soulbound Token (On-Chain Green CV). + /// Note: No transfer functions exist in this contract, making it strictly Soulbound. + +```rust +pub fn mint_impact_sbt( + env: Env, + to: Address, + carbon_saved: i128, + reliability_score: u32, + ) +``` + +### pub fn get_sbt + +/// View function to fetch a user's On-Chain Green CV + +```rust +pub fn get_sbt(env: Env, user: Address) -> Option +``` + +## utility_contracts\src\secure_call_interface.rs + +### pub struct ContractCallConfig + +```rust +pub struct ContractCallConfig +``` + +### pub struct CallResult + +```rust +pub struct CallResult +``` + +### pub enum SecureCallError + +```rust +pub enum SecureCallError +``` + +### pub enum SecureCallDataKey + +```rust +pub enum SecureCallDataKey +``` + +### pub struct SecureCallManager + +```rust +pub struct SecureCallManager +``` + +### pub fn initialize + +/// Initialize the secure call manager + +```rust +pub fn initialize(env: Env, admin: Address) +``` + +### pub fn secure_call + +/// Execute a secure cross-contract call with comprehensive security checks + +```rust +pub fn secure_call( + env: &Env, + target_contract: &Address, + function: &Symbol, + args: Vec, + gas_limit: Option, + ) -> Result +``` + +### pub fn register_contract + +/// Register a contract for secure calls + +```rust +pub fn register_contract( + env: &Env, + contract_address: &Address, + allowed_functions: Vec, + max_gas_per_call: Option, + requires_auth: bool, + ) +``` + +### pub fn unregister_contract + +/// Remove a contract from the whitelist + +```rust +pub fn unregister_contract(env: &Env, contract_address: &Address) +``` + +### pub fn update_contract_config + +/// Update contract configuration + +```rust +pub fn update_contract_config( + env: &Env, + contract_address: &Address, + allowed_functions: Option>, + max_gas_per_call: Option, + requires_auth: Option, + enabled: Option, + ) +``` + +### pub fn get_contract_config + +/// Get contract configuration + +```rust +pub fn get_contract_config( + env: &Env, + contract_address: &Address, + ) -> Option +``` + +### pub fn is_function_allowed + +/// Check if a contract is whitelisted for a specific function + +```rust +pub fn is_function_allowed(env: &Env, contract_address: &Address, function: &Symbol) -> bool +``` + +### pub fn emergency_disable + +/// Emergency disable all cross-contract calls + +```rust +pub fn emergency_disable(env: &Env) +``` + +### pub fn emergency_enable + +/// Re-enable cross-contract calls (admin only) + +```rust +pub fn emergency_enable(env: &Env) +``` + +## utility_contracts\src\secure_call_interface_old.rs + +### pub struct ContractCallConfig + +```rust +pub struct ContractCallConfig +``` + +### pub struct CallResult + +```rust +pub struct CallResult +``` + +### pub enum SecureCallError + +```rust +pub enum SecureCallError +``` + +### pub enum SecureCallDataKey + +```rust +pub enum SecureCallDataKey +``` + +### pub struct SecureCallManager + +/// Implementation of the secure call interface + +```rust +pub struct SecureCallManager +``` + +### pub fn initialize + +/// Initialize the secure call manager + +```rust +pub fn initialize(env: Env, admin: Address) +``` + +### pub fn secure_call + +/// Execute a secure cross-contract call with comprehensive security checks + +```rust +pub fn secure_call( + env: &Env, + target_contract: &Address, + function: &Symbol, + args: Vec< soroban_sdk::Val >, + gas_limit: Option, + ) -> Result, SecureCallError> +``` + +### pub fn register_contract + +/// Register a contract for secure calls + +```rust +pub fn register_contract( + env: &Env, + contract_address: &Address, + allowed_functions: Vec, + max_gas_per_call: Option, + requires_auth: bool, + ) +``` + +### pub fn unregister_contract + +/// Remove a contract from the whitelist + +```rust +pub fn unregister_contract(env: &Env, contract_address: &Address) +``` + +### pub fn update_contract_config + +/// Update contract configuration + +```rust +pub fn update_contract_config( + env: &Env, + contract_address: &Address, + allowed_functions: Option>, + max_gas_per_call: Option, + requires_auth: Option, + enabled: Option, + ) +``` + +### pub fn get_contract_config + +/// Get contract configuration + +```rust +pub fn get_contract_config(env: &Env, contract_address: &Address) -> Option +``` + +### pub fn is_function_allowed + +/// Check if a contract is whitelisted for a specific function + +```rust +pub fn is_function_allowed(env: &Env, contract_address: &Address, function: &Symbol) -> bool +``` + +### pub fn emergency_disable + +/// Emergency disable all cross-contract calls + +```rust +pub fn emergency_disable(env: &Env) +``` + +### pub fn emergency_enable + +/// Re-enable cross-contract calls (admin only) + +```rust +pub fn emergency_enable(env: &Env) +``` + +## utility_contracts\src\sep40_streaming.rs + +### pub struct OraclePrice + +```rust +pub struct OraclePrice +``` + +### pub fn get_oracle_price + +```rust +pub fn get_oracle_price(env: &Env, oracle: Address) -> OraclePrice +``` + +### pub fn adjust_stream_rate + +```rust +pub fn adjust_stream_rate(env: &Env, user: Address, oracle: Address, fiat_rate_per_kwh: i128) -> i128 +``` + +### pub fn bill_user + +```rust +pub fn bill_user(env: &Env, user: Address, oracle: Address, fiat_rate_per_kwh: i128, consumption: i128) +``` + +## utility_contracts\src\settlement.rs + +### pub enum SettlementError + +```rust +pub enum SettlementError +``` + +### pub struct SettlementContract + +```rust +pub struct SettlementContract +``` + +### pub fn propose_settlement + +/// Propose a new settlement + /// + /// # Arguments + /// * `env` - The contract environment + /// * `proposal_id` - Unique identifier for the proposal + /// * `payer` - Address of the payer + /// * `payee` - Address of the payee + /// * `amount` - Amount to be settled + /// * `rate` - Exchange rate + /// * `settlement_window` - Time window in seconds (must be between 60 and 604800) + /// * `token_address` - Token contract address for locking + +```rust +pub fn propose_settlement( + env: Env, + proposal_id: u64, + payer: Address, + payee: Address, + amount: i128, + rate: i128, + settlement_window: u64, + token_address: Address, + ) -> SettlementProposal +``` + +### pub fn finalize_settlement + +/// Finalize a settlement + /// + /// # Arguments + /// * `env` - The contract environment + /// * `proposal_id` - ID of the proposal to finalize + /// * `token_address` - Token contract address for unlocking + /// + /// # Panics + /// * If the current ledger timestamp exceeds the settlement deadline (DeadlineExceeded) + /// * If the proposal is not found (ProposalNotFound) + /// * If the proposal is already finalized (AlreadyFinalized) + +```rust +pub fn finalize_settlement( + env: Env, + proposal_id: u64, + token_address: Address, + ) +``` + +### pub fn get_proposal + +/// Get a settlement proposal by ID + +```rust +pub fn get_proposal(env: Env, proposal_id: u64) -> Option +``` + +### pub fn is_deadline_exceeded + +/// Check if a proposal deadline has passed + +```rust +pub fn is_deadline_exceeded(env: Env, proposal_id: u64) -> bool +``` + +## utility_contracts\src\settlement_lock_manager.rs + +### pub fn lock_resources + +/// Lock resources for a settlement proposal + +```rust +pub fn lock_resources(env: &Env, proposal: &mut SettlementProposal, token_address: &Address) +``` + +### pub fn unlock_resources + +/// Unlock/release locked resources for a settlement proposal + +```rust +pub fn unlock_resources(env: &Env, proposal: &mut SettlementProposal, token_address: &Address) +``` + +### pub fn release_locked_resources + +/// Release locked resources - alias for unlock_resources for clarity + +```rust +pub fn release_locked_resources(env: &Env, proposal: &mut SettlementProposal, token_address: &Address) +``` + +## utility_contracts\src\settlement_types.rs + +### pub struct SettlementProposal + +```rust +pub struct SettlementProposal +``` + +### pub fn new + +```rust +pub fn new( + proposal_id: u64, + payer: Address, + payee: Address, + amount: i128, + rate: i128, + submission_timestamp: u64, + settlement_window: u64, + ) -> Self +``` + +## utility_contracts\src\stream.rs + +### pub struct Stream + +```rust +pub struct Stream +``` + +### pub struct RateChangeProposal + +```rust +pub struct RateChangeProposal +``` + +## utility_contracts\src\tamper_detection.rs + +### pub struct DeviceState + +```rust +pub struct DeviceState +``` + +### pub fn handle_tamper_signal + +```rust +pub fn handle_tamper_signal(env: &Env, device: Address) +``` + +### pub fn is_blacklisted + +```rust +pub fn is_blacklisted(env: &Env, device: Address) -> bool +``` + +## utility_contracts\src\tariff_oracle.rs + +### pub struct TariffWindowTransition + +```rust +pub struct TariffWindowTransition +``` + +### pub enum TariffTier + +```rust +pub enum TariffTier +``` + +### pub struct HourlyTariff + +```rust +pub struct HourlyTariff +``` + +### pub struct DailyTariffSchedule + +```rust +pub struct DailyTariffSchedule +``` + +### pub struct TariffUpdateProposal + +```rust +pub struct TariffUpdateProposal +``` + +### pub struct FlowCalculationResult + +```rust +pub struct FlowCalculationResult +``` + +### pub struct TariffOracle + +/// Main contract implementation for the Ledger-Native Utility-Tariff Price Oracle. +/// +/// This contract provides a sophisticated Time-of-Use (ToU) pricing system that enables +/// utility companies to implement dynamic pricing based on the hour of the day. The oracle +/// stores 24-hour pricing schedules on-chain and provides seamless rate interpolation +/// for streams that span across multiple tariff windows. +/// +/// ## Key Responsibilities +/// +/// - **Tariff Schedule Management**: Store and update 24-hour pricing schedules +/// - **Rate Calculation**: Provide real-time rate calculations for any time period +/// - **Consumer Protection**: Enforce 24-hour notice period for price changes +/// - **Security**: Ensure only authorized administrators can modify pricing +/// - **Transparency**: Maintain complete audit trail of all tariff changes +/// +/// ## Security Guarantees +/// +/// - **Signed Updates**: All tariff changes must be cryptographically signed +/// - **Notice Period**: 24-hour advance notice prevents surprise price changes +/// - **Access Control**: Only authorized Grid Administrators can modify pricing +/// - **Audit Trail**: Complete history of all proposals and executions +/// - **Integrity**: Hash verification prevents tampering with schedule data +/// +/// # Issue Reference +/// +/// Implements Issue #261: Ledger-Native "Utility-Tariff" Price Oracle + +```rust +pub struct TariffOracle +``` + +### pub fn initialize + +/// Initializes the tariff oracle with a grid administrator and initial schedule. + +```rust +pub fn initialize(env: Env, grid_admin: Address, initial_schedule: DailyTariffSchedule) +``` + +### pub fn propose_tariff_update + +/// Submit new tariff schedule with 24-hour notice period + /// + /// # Arguments + /// * `env` - The contract environment + /// * `new_schedule` - New daily tariff schedule + /// * `admin_signature` - Grid administrator's signature + /// + /// # Errors + /// * `ContractError::UnauthorizedAdmin` - if not grid admin + /// * `ContractError::InvalidTariffSchedule` - if schedule is invalid + +```rust +pub fn propose_tariff_update( + env: Env, + new_schedule: DailyTariffSchedule, + admin_signature: soroban_sdk::BytesN<64>, + ) -> u64 +``` + +### pub fn execute_tariff_update + +/// Execute a tariff update proposal (after notice period) + /// + /// # Arguments + /// * `env` - The contract environment + /// * `proposal_id` - ID of proposal to execute + /// + /// # Errors + /// * `ContractError::UnauthorizedAdmin` - if not grid admin + /// * `ContractError::AdminExecutionWindowExpired` - if notice period not met + +```rust +pub fn execute_tariff_update(env: Env, proposal_id: u64) +``` + +### pub fn calculate_current_flow_rate + +/// Calculate flow rate for current time with Time-of-Use pricing + /// + /// # Arguments + /// * `env` - The contract environment + /// * `consumption_rate` - Device consumption rate + /// + /// # Returns + /// Current tokens per second based on tariff + +```rust +pub fn calculate_current_flow_rate(env: Env, consumption_rate: i128) -> i128 +``` + +### pub fn calculate_flow_for_period + +/// Calculate flow for a time period that may span multiple tariff windows + /// + /// # Arguments + /// * `env` - The contract environment + /// * `start_timestamp` - Start time of the period + /// * `end_timestamp` - End time of the period + /// * `consumption_rate` - Constant consumption rate + /// + /// # Returns + /// Flow calculation result with blended rates + +```rust +pub fn calculate_flow_for_period( + env: Env, + start_timestamp: u64, + end_timestamp: u64, + consumption_rate: i128, + ) -> FlowCalculationResult +``` + +### pub fn get_current_tariff + +/// Get current tariff for the given hour + /// + /// # Arguments + /// * `env` - The contract environment + /// * `hour` - Hour of day (0-23) + /// + /// # Returns + /// Hourly tariff for the specified hour + +```rust +pub fn get_current_tariff(env: Env, hour: u8) -> HourlyTariff +``` + +### pub fn get_current_schedule + +```rust +pub fn get_current_schedule(env: Env) -> DailyTariffSchedule +``` + +### pub fn is_configured + +```rust +pub fn is_configured(env: Env) -> bool +``` + +### pub fn get_grid_admin + +```rust +pub fn get_grid_admin(env: Env) -> Address +``` + +### pub fn get_tariff_proposal + +```rust +pub fn get_tariff_proposal(env: Env, proposal_id: u64) -> TariffUpdateProposal +``` + +### pub enum DataKey + +```rust +pub enum DataKey +``` + +## utility_contracts\src\temporary_storage.rs + +### pub enum TempStorageKey + +```rust +pub enum TempStorageKey +``` + +### pub struct TempStorageManager + +/// Temporary Storage Manager + +```rust +pub struct TempStorageManager +``` + +### pub fn store_flow_accumulation + +/// Store flow accumulation data temporarily + +```rust +pub fn store_flow_accumulation(env: &Env, stream_id: u64, accumulation: i128, timestamp: u64) +``` + +### pub fn get_flow_accumulation + +/// Get flow accumulation from temporary storage + +```rust +pub fn get_flow_accumulation(env: &Env, stream_id: u64) -> Option<(i128, u64)> +``` + +### pub fn store_meter_usage_delta + +/// Store meter usage delta temporarily + +```rust +pub fn store_meter_usage_delta(env: &Env, meter_id: u64, usage_delta: i128, timestamp: u64) +``` + +### pub fn get_and_clear_meter_usage_delta + +/// Get and clear meter usage delta + +```rust +pub fn get_and_clear_meter_usage_delta(env: &Env, meter_id: u64) -> Option<(i128, u64)> +``` + +### pub fn store_provider_window + +/// Store provider withdrawal window temporarily + +```rust +pub fn store_provider_window(env: &Env, provider: &Address, window: &ProviderWithdrawalWindow) +``` + +### pub fn get_provider_window + +/// Get provider withdrawal window from temporary storage + +```rust +pub fn get_provider_window(env: &Env, provider: &Address) -> Option +``` + +### pub fn store_dust_delta + +/// Store dust aggregation delta temporarily + +```rust +pub fn store_dust_delta(env: &Env, token: &Address, dust_delta: i128) +``` + +### pub fn get_and_clear_dust_delta + +/// Get and clear dust aggregation delta + +```rust +pub fn get_and_clear_dust_delta(env: &Env, token: &Address) -> Option +``` + +### pub fn store_sla_delta + +/// Store SLA penalty delta temporarily + +```rust +pub fn store_sla_delta(env: &Env, meter_id: u64, penalty_delta: u64) +``` + +### pub fn get_and_clear_sla_delta + +/// Get and clear SLA penalty delta + +```rust +pub fn get_and_clear_sla_delta(env: &Env, meter_id: u64) -> Option +``` + +### pub fn store_fee_delta + +/// Store streaming fee delta temporarily + +```rust +pub fn store_fee_delta(env: &Env, stream_id: u64, fee_delta: i128) +``` + +### pub fn get_and_clear_fee_delta + +/// Get and clear streaming fee delta + +```rust +pub fn get_and_clear_fee_delta(env: &Env, stream_id: u64) -> Option +``` + +### pub fn store_batch_data + +/// Store batch operation data + +```rust +pub fn store_batch_data(env: &Env, operation: Symbol, data: &soroban_sdk::Val) +``` + +### pub fn get_batch_data> + +/// Get batch operation data + +```rust +pub fn get_batch_data>(env: &Env, operation: Symbol) -> Option +``` + +### pub fn clear_batch_data + +/// Clear batch operation data + +```rust +pub fn clear_batch_data(env: &Env, operation: Symbol) +``` + +### pub fn flush_to_persistent + +/// Flush all temporary data to persistent storage + /// This should be called at the end of batch operations + +```rust +pub fn flush_to_persistent(env: &Env) +``` + +### pub struct OptimizedFlowCalculator + +/// Optimized Flow Calculator using temporary storage + +```rust +pub struct OptimizedFlowCalculator +``` + +### pub fn calculate_with_temp_storage + +/// Calculate flow accumulation using temporary storage to reduce writes + +```rust +pub fn calculate_with_temp_storage( + env: &Env, + flow: &ContinuousFlow, + current_timestamp: u64, + ) -> i128 +``` + +### pub struct OptimizedUsageTracker + +/// Optimized Usage Tracker using temporary storage + +```rust +pub struct OptimizedUsageTracker +``` + +### pub fn track_usage_with_temp_storage + +/// Track usage changes using temporary storage to reduce persistent writes + +```rust +pub fn track_usage_with_temp_storage( + env: &Env, + meter_id: u64, + usage_delta: i128, + timestamp: u64, + ) +``` + +## utility_contracts\src\velocity_limit.rs + +### pub struct DailyOutflow + +```rust +pub struct DailyOutflow +``` + +### pub struct GlobalOutflowTracker + +```rust +pub struct GlobalOutflowTracker +``` + +### pub struct VelocityOverride + +```rust +pub struct VelocityOverride +``` + +### pub struct VelocityConfig + +```rust +pub struct VelocityConfig +``` + +### pub struct AnomalousActivity + +```rust +pub struct AnomalousActivity +``` + +### pub struct OverrideApplied + +```rust +pub struct OverrideApplied +``` + +### pub struct DailyResetOccurred + +```rust +pub struct DailyResetOccurred +``` + +### pub enum VelocityDataKey + +```rust +pub enum VelocityDataKey +``` + +### pub fn get_day_boundary + +/// Calculate day boundary from timestamp +/// Returns the Unix timestamp for the start of the day containing `timestamp` + +```rust +pub fn get_day_boundary(timestamp: u64) -> u64 +``` + +### pub fn is_new_day + +/// Check if a new day has started since the given window start + +```rust +pub fn is_new_day(window_start: u64, current_timestamp: u64) -> bool +``` + +### pub fn get_day_number + +/// Calculate day number since epoch (for analytics) + +```rust +pub fn get_day_number(timestamp: u64) -> u64 +``` + +### pub fn check_per_stream_velocity + +/// Check if a withdrawal would exceed per-stream velocity limit +/// +/// Returns: +/// - `Ok(())` if withdrawal is allowed +/// - `Err` if velocity limit would be exceeded + +```rust +pub fn check_per_stream_velocity( + env: &Env, + meter_id: u64, + provider: &Address, + withdrawal_amount: i128, +) -> Result<(), soroban_sdk::Symbol> +``` + +### pub fn check_global_velocity + +/// Check if withdrawal would exceed global (system-wide) velocity limit +/// +/// Returns: +/// - `Ok(())` if withdrawal is allowed +/// - `Err` if global velocity limit would be exceeded + +```rust +pub fn check_global_velocity( + env: &Env, + withdrawal_amount: i128, + provider: &Address, +) -> Result<(), soroban_sdk::Symbol> +``` + +### pub fn check_velocity_limits + +/// Check both per-stream and global velocity limits +/// +/// This is the main entry point for velocity limit validation + +```rust +pub fn check_velocity_limits( + env: &Env, + meter_id: u64, + provider: &Address, + withdrawal_amount: i128, +) -> Result<(), soroban_sdk::Symbol> +``` + +### pub fn apply_override + +/// Apply admin override to suspend velocity limits +/// +/// `scope` = 0 for global override, or specific meter_id +/// `expires_at` = 0 for permanent, or Unix timestamp for expiration +/// NOTE: Caller (lib.rs) must have already verified admin auth against stored AdminAddress. + +```rust +pub fn apply_override(env: &Env, admin: Address, scope: u64, expires_at: u64, reason: Symbol) +``` + +### pub fn revoke_override + +/// Revoke an active override + +```rust +pub fn revoke_override(env: &Env, scope: u64) +``` + +### pub fn get_velocity_config + +/// Get current velocity configuration + +```rust +pub fn get_velocity_config(env: &Env) -> Option +``` + +### pub fn set_velocity_config + +/// Update velocity configuration (admin only - caller must have verified admin auth) + +```rust +pub fn set_velocity_config(env: &Env, _admin: Address, config: VelocityConfig) +``` + + + +## 4. Protocol Subsystems + +### Source: `docs/STREAM_INSURANCE_POOL_GOVERNANCE.md` + +### Stream Insurance Pool Governance System + +#### Overview + +The Stream Insurance Pool Governance system implements a decentralized "Community Insurance" mechanism that provides mutual aid for utility security. Users can opt into a shared insurance pool by paying premiums, and the pool automatically lends funds to members whose utility streams are about to fail due to missed deposits. + +#### Key Features + +##### 1. Community Mutual Aid +- **Pooled Safety Buffer**: Multiple users contribute to a shared insurance fund +- **Auto-Lending**: Automatic emergency funding when member streams are at risk +- **Risk Sharing**: Distributes individual risk across the community +- **Decentralized Governance**: Pool participants vote on key parameters + +##### 2. Risk-Based Premium Calculation +- **Dynamic Pricing**: Premiums calculated based on individual risk assessment +- **Multi-Factor Risk Scoring**: Considers payment history, usage patterns, device security, and tenure +- **Fair Pricing**: Lower-risk users pay lower premiums, higher-risk users pay more +- **Transparent Scoring**: Risk factors are clearly defined and auditable + +##### 3. Governance Mechanisms +- **Proposal System**: Members can propose changes to pool parameters +- **Voting Power**: Based on premium contributions and tenure in the pool +- **Quorum Requirements**: 20% of voting power must participate for valid decisions +- **Approval Threshold**: 51% approval required for proposal execution +- **Timelock**: 7-day voting period ensures deliberate decision-making + +#### Architecture + +##### Core Components + +###### InsurancePool +```rust +pub struct InsurancePool { + pub total_funds: i128, // Total pool balance + pub total_members: u32, // Number of active members + pub total_voting_power: i128, // Sum of all member voting power + pub created_at: u64, // Pool creation timestamp + pub governance_admin: Address, // Initial admin (can be changed via governance) + pub base_premium_rate_bps: i128, // Base premium rate (basis points) + pub risk_multiplier_max: i128, // Maximum risk multiplier + pub is_active: bool, // Pool operational status + pub emergency_pause: bool, // Emergency pause flag +} +``` + +###### InsurancePoolMember +```rust +pub struct InsurancePoolMember { + pub user: Address, // Member's address + pub premium_paid: i128, // Total premium contributed + pub join_timestamp: u64, // When member joined + pub last_claim_timestamp: u64, // Last claim submission time + pub claim_count: u32, // Number of claims made + pub risk_score: u32, // Current risk score (0-1000) + pub voting_power: i128, // Calculated voting power + pub is_active: bool, // Member status +} +``` + +###### GovernanceProposal +```rust +pub struct GovernanceProposal { + pub proposal_id: u64, // Unique proposal identifier + pub proposer: Address, // Who created the proposal + pub proposal_type: ProposalType, // Type of change proposed + pub description: Symbol, // Brief description + pub new_value: i128, // Proposed new value + pub created_at: u64, // Creation timestamp + pub voting_deadline: u64, // When voting ends + pub votes_for: i128, // Voting power supporting + pub votes_against: i128, // Voting power opposing + pub total_votes: i128, // Total voting power participated + pub is_executed: bool, // Whether proposal was executed + pub is_cancelled: bool, // Whether proposal was cancelled +} +``` + +##### Risk Assessment System + +The system evaluates member risk across four dimensions: + +1. **Payment History Score (0-250 points)** + - PrePaid: Balance maintenance patterns + - PostPaid: Debt-to-collateral ratios + - Consistent positive balances = higher score + +2. **Usage Stability Score (0-250 points)** + - Peak usage vs. average usage ratios + - Stable consumption patterns = higher score + - High volatility = lower score + +3. **Device Security Score (0-250 points)** + - Device pairing status + - Heartbeat frequency and recency + - Proper cryptographic setup = higher score + +4. **Tenure Score (0-250 points)** + - Length of membership in pool + - Account age and history + - Longer tenure = higher score + +**Total Risk Score**: Sum of all dimensions (0-1000) +- Lower scores indicate lower risk +- Used to calculate premium multipliers (0.5x - 3.0x) + +##### Premium Calculation + +``` +Base Premium = Monthly Usage Value × Base Premium Rate (BPS) +Risk Multiplier = 0.5 + (Risk Score / 1000) × 2.5 +Final Premium = Base Premium × Risk Multiplier +``` + +Constraints: +- Minimum Premium: 100 XLM +- Maximum Premium: 10,000 XLM +- Base Rate Range: 0.1% - 10% of monthly usage + +##### Claim Processing + +###### Automatic Approval +Small claims are automatically approved and processed if: +- Claim amount ≤ 1% of total pool funds +- Member risk score ≤ 300 (low risk) +- Member is in good standing + +###### Manual Review Process +Larger claims require governance approval: +1. Member submits claim with reason +2. Community reviews claim details +3. Voting period for approval/rejection +4. If approved, funds are transferred + +###### Claim Limits +- Maximum claim: 10% of total pool funds +- Cooldown period: 30 days between claims +- Emergency override: Governance can approve exceptions + +##### Governance Proposal Types + +1. **ChangePremiumRate**: Adjust base premium percentage +2. **ChangeRiskMultiplier**: Modify maximum risk multiplier +3. **ChangeMaxClaimAmount**: Adjust maximum claim limits +4. **AddMember**: Approve new member applications +5. **RemoveMember**: Remove problematic members +6. **EmergencyPause**: Pause pool operations +7. **ChangeGovernanceAdmin**: Transfer admin rights + +##### Integration with Utility Contracts + +###### Fee Allocation +- 0.5% of every utility claim is allocated to the insurance pool +- Provides sustainable funding for the pool +- Creates alignment between utility usage and insurance funding + +###### Emergency Funding +When a member's utility stream is at risk: +1. System detects low balance or payment failure +2. If member is in insurance pool, automatic claim is triggered +3. Funds are transferred to member's meter balance +4. Member's claim history is updated + +###### Throttling Integration +- Insurance pool members get priority during network throttling +- Pool membership considered in priority calculations +- Provides additional utility security benefit + +#### Usage Examples + +##### Creating an Insurance Pool + +```rust +// Admin creates the pool with 1% base premium rate +UtilityContract::create_insurance_pool( + env, + admin_address, + 100, // 1% in basis points +)?; +``` + +##### Joining the Pool + +```rust +// Calculate required premium for user's meter +let premium = UtilityContract::calculate_premium_amount( + env, + user_address, + meter_id, +)?; + +// Join the pool +UtilityContract::join_insurance_pool( + env, + user_address, + meter_id, + premium, +)?; +``` + +##### Submitting a Claim + +```rust +// Submit emergency funding claim +let claim_id = UtilityContract::submit_insurance_claim( + env, + claimant_address, + meter_id, + requested_amount, + symbol_short!("EmergFund"), +)?; +``` + +##### Creating Governance Proposals + +```rust +// Propose to change premium rate to 1.5% +let proposal_id = UtilityContract::create_governance_proposal( + env, + proposer_address, + ProposalType::ChangePremiumRate, + symbol_short!("NewRate"), + 150, // 1.5% in basis points +)?; +``` + +##### Voting on Proposals + +```rust +// Vote in favor of the proposal +UtilityContract::vote_on_proposal( + env, + voter_address, + proposal_id, + true, // vote for +)?; +``` + +#### Security Considerations + +##### Access Control +- Only pool members can vote on proposals +- Minimum voting power required to create proposals (5% of total) +- Cooldown periods prevent spam claims +- Emergency pause mechanism for crisis situations + +##### Economic Security +- Risk-based pricing prevents adverse selection +- Claim limits prevent pool drainage +- Diversified risk across multiple members +- Sustainable funding through utility fee allocation + +##### Governance Security +- Quorum requirements prevent minority control +- Voting power based on stake and tenure +- Timelock periods allow for deliberation +- Transparent proposal and voting process + +#### Benefits + +##### For Individual Users +- **Utility Security**: Protection against service interruption +- **Lower Individual Risk**: Shared risk across community +- **Governance Participation**: Voice in pool management +- **Priority Access**: Benefits during network congestion + +##### For the Ecosystem +- **Network Stability**: Reduced service interruptions +- **Community Building**: Shared incentives and cooperation +- **Sustainable Funding**: Self-funding through utility fees +- **Decentralized Governance**: Community-controlled parameters + +##### For Utility Providers +- **Reduced Defaults**: Insurance covers payment gaps +- **Stable Revenue**: More predictable payment flows +- **Customer Retention**: Enhanced service reliability +- **Risk Mitigation**: Shared responsibility for customer defaults + +#### Future Enhancements + +##### Advanced Risk Models +- Machine learning-based risk assessment +- Integration with external credit scoring +- Dynamic risk adjustment based on market conditions +- Predictive analytics for claim probability + +##### Cross-Pool Insurance +- Multiple specialized pools (residential, commercial, industrial) +- Inter-pool reinsurance mechanisms +- Risk transfer between pools +- Specialized coverage types + +##### Integration Expansions +- Integration with DeFi lending protocols +- Automated market makers for premium pricing +- Tokenized insurance positions +- Cross-chain insurance coverage + +#### Conclusion + +The Stream Insurance Pool Governance system creates a robust, community-driven mutual aid mechanism that enhances utility security while maintaining decentralized governance. By combining risk-based pricing, democratic decision-making, and automatic emergency funding, it provides a sustainable solution for utility payment security that benefits all participants in the ecosystem. + +### Source: `contracts/utility_contracts/src/GASLESS_RELAY_DOCS.md` + +### Gasless Transaction Relay - Issue #131 + +#### Overview + +The Gasless Transaction Relay is an EIP-2771 compatible system that enables the Utility Protocol to sponsor gas costs for approved operations during user onboarding. This removes friction for new users who would otherwise need to acquire XLM to pay gas fees. + +#### Architecture + +##### Core Components + +1. **Gasless Relay (`gasless_relay.rs`)** + - Main contract implementing EIP-2771 meta-transaction forwarding + - Manages nonce counters for replay protection + - Tracks sponsorship pool balance + - Forwards authenticated meta-transactions + +2. **Signature Verification (`gasless_relay_sig_verify.rs`)** + - Ed25519 signature verification for meta-transactions + - Timestamp validation (6-hour window) + - Approved forwarder validation + - Signer address recovery + - Nonce-based replay attack prevention + +3. **Sponsorship Policy Engine (`gasless_relay_policy.rs`)** + - Fine-grained control over which operations are sponsored + - Multiple sponsorship statuses (FullySponsored, PartiallySponsored, Suspended) + - Per-operation gas limits + - Daily transaction rate limiting + - Operation statistics tracking + - Policy suspension/resumption for operational control + +#### Key Features + +##### 1. EIP-2771 Compatibility + +The relay implements the EIP-2771 standard for meta-transactions: + +``` +User → signs meta-transaction → Forwarder → Relay contract → Target contract +``` + +Meta-transactions include: +- `from` - Originating user address +- `to` - Target contract address +- `value` - XLM amount in stroops +- `data` - Encoded function call +- `nonce` - For replay protection +- `gas_price` - Gas price offered +- `gas_limit` - Maximum gas +- `deadline` - Transaction deadline + +##### 2. Replay Attack Prevention + +- **Nonce Management**: Each user has a sequential nonce that must increment +- **Forwarder Nonce Tracking**: Forwarders have independent nonce sequences +- **Signature Aging**: Signatures are only valid for 6 hours + +##### 3. Rate Limiting + +- **Per-User Limits**: Configurable maximum transactions per time period +- **Per-Operation Limits**: Daily limits on specific operations +- **Dynamic Period Tracking**: Automatic reset at period boundaries + +##### 4. Sponsorship Pool Management + +- **Balance Tracking**: Real-time pool balance monitoring +- **Deduction on Use**: Immediate deduction from pool when transaction is sponsored +- **Top-Up Functionality**: Admin can add funds to pool +- **Low Balance Warnings**: Can trigger alerts when balance is low + +##### 5. Flexible Sponsorship Policies + +```rust +DetailedSponsorshipPolicy { + operation_id: Symbol, // Operation identifier + status: SponsorshipStatus, // Full, Partial, Suspended, NotSponsored + max_gas: u64, // Maximum gas per transaction + sponsorship_percentage: u32, // 0-100% sponsorship + daily_tx_limit: u32, // Transactions per day + cost_per_tx: i128, // Cost in stroops + created_at: u64, // Timestamp + updated_at: u64, // Last update + admin: Address, // Policy administrator + description: String, // Human-readable description +} +``` + +#### Usage Flow + +##### 1. Initialize the Relay + +```rust +relay.initialize(env, admin_address, initial_pool_balance)?; +``` + +##### 2. Register Trusted Forwarder + +```rust +relay.register_forwarder(env, forwarder_address, public_key)?; +``` + +##### 3. Create Sponsorship Policies + +```rust +relay.register_sponsorship_policy(env, operation_id, policy)?; +``` + +##### 4. Set Rate Limits + +```rust +relay.set_rate_limit(env, user_address, max_txs_per_period, period_seconds)?; +``` + +##### 5. Forward Meta-Transaction + +```rust +relay.forward_meta_transaction(env, meta_tx_request, signature)?; +``` + +#### Error Handling + +The relay defines comprehensive error codes: + +| Error | Code | Meaning | +|-------|------|---------| +| InvalidSignature | 1 | Signature verification failed | +| NonceMismatch | 2 | Nonce is invalid or replay attempted | +| RateLimitExceeded | 3 | User exceeded rate limit | +| OperationNotSponsored | 4 | Operation not in approved list | +| UntrustedForwarder | 5 | Forwarder not in whitelist | +| DeadlineExpired | 6 | Meta-transaction deadline passed | +| InsufficientSponsorshipBalance | 7 | Pool doesn't have enough funds | +| PolicyRejected | 8 | Operation failed policy check | + +#### Security Considerations + +##### 1. Signature Verification + +- Ed25519 signatures verified before processing +- Timestamps checked to prevent replay via old signatures +- Public key validation against approved forwarders + +##### 2. Nonce Management + +- Sequential nonce enforcement prevents replay attacks +- Nonce increments required for each new transaction +- Independent nonce sequences for different forwarders + +##### 3. Rate Limiting + +- Prevents denial-of-service through sponsorship abuse +- Per-user and per-operation limits +- Automatic reset at period boundaries + +##### 4. Access Control + +- Admin-only operations for configuration +- Policy management restricted to admins +- Forwarder registration requires admin approval + +##### 5. Pool Protection + +- Balance checks before each transaction +- Immediate deduction to prevent double-spending +- Emergency suspension capability for policies + +#### Operational Procedures + +##### Monitoring Pool Balance + +```rust +let balance = relay.get_sponsorship_pool_balance(env); +if balance < CRITICAL_THRESHOLD { + // Alert admin +} +``` + +##### Checking User Nonce + +```rust +let current_nonce = relay.get_nonce(env, user_address); +``` + +##### Suspending an Operation + +```rust +engine.suspend_policy(env, operation_id)?; +``` + +##### Resuming an Operation + +```rust +engine.resume_policy(env, operation_id, SponsorshipStatus::FullySponsored)?; +``` + +##### Topping Up Sponsorship Pool + +```rust +relay.top_up_sponsorship_pool(env, amount)?; +``` + +#### Performance Characteristics + +- **Nonce Lookup**: O(1) per user +- **Policy Lookup**: O(n) where n = number of policies (typically small) +- **Rate Limit Check**: O(1) per user +- **Signature Verification**: Native Soroban crypto (optimized) + +#### Testing + +The implementation includes three levels of tests: + +1. **Unit Tests** (`gasless_relay_tests.rs`) + - 15 focused test cases + - Tests individual component functionality + - Validates error conditions + +2. **Integration Tests** (`gasless_relay_integration_tests.rs`) + - 16 comprehensive scenarios + - Tests cross-component interactions + - Validates complete workflows + - Covers edge cases and recovery scenarios + +3. **Property Tests** + - Can be added using quickcheck for fuzzing + - Test invariants across random inputs + +#### Future Enhancements + +1. **Batch Meta-Transactions**: Support multiple operations per relay call +2. **Conditional Sponsorship**: Sponsor only if certain conditions are met +3. **Dynamic Pricing**: Adjust sponsorship costs based on pool balance +4. **Multi-Sig Approval**: Require multiple admins to top up pool +5. **Statistics Export**: Detailed reporting on sponsorship usage +6. **Cross-Contract Integration**: Relay calls to other contracts +7. **Emergency Mode**: Pause all sponsorship without suspending policies + +#### Governance + +- **Admin Role**: Controls initialization, policy creation, forwarder registration +- **Policy Admin**: Can suspend/resume individual policies +- **Monitor Role**: Can check pool balance and statistics (future) + +#### Cost Analysis + +Each sponsored transaction costs: +- Storage: ~100 bytes per rate limit entry +- Computation: ~5,000 stroops for relay overhead +- Gas: Depends on underlying operation + +Total cost to protocol: `sponsorship_cost + relay_overhead + gas_cost` + +#### Migration Guide + +##### Deploying to Production + +1. Deploy relay contract +2. Register trusted forwarders +3. Create initial sponsorship policies +4. Set conservative rate limits +5. Fund sponsorship pool +6. Monitor usage patterns +7. Adjust policies based on metrics + +##### Rollback Plan + +- Suspend all policies: `suspend_policy()` for each +- Redirect traffic: Update forwarder configuration +- Drain pool: `get_sponsorship_pool_balance()` and manual transfer +- Disable relay: Empty pool, remove all policies + +#### References + +- **EIP-2771**: https://eips.ethereum.org/EIPS/eip-2771 +- **Soroban Documentation**: https://developers.stellar.org/soroban +- **Project Issue #131**: Gasless Transaction Relay for User Onboarding + + +### Source: `SETTLEMENT_IMPLEMENTATION.md` + +### Settlement Contract Implementation + +#### Overview +This document describes the implementation of the settlement deadline enforcement feature as specified in the requirements. + +#### Files Created + +##### 1. `contracts/utility_contracts/src/settlement_types.rs` +Contains the `SettlementProposal` struct with all required fields: +- `proposal_id`: Unique identifier +- `payer`: Address of the payer +- `payee`: Address of the payee +- `amount`: Amount to be settled +- `rate`: Exchange rate at proposal time +- `submission_timestamp`: u64 epoch seconds when submitted +- `settlement_deadline`: u64 epoch seconds deadline (submission_timestamp + settlement_window) +- `finalized`: Boolean flag +- `resources_locked`: Boolean flag for resource locking + +##### 2. `contracts/utility_contracts/src/settlement_lock_manager.rs` +Resource lock management functions: +- `lock_resources()`: Locks resources when proposal is created +- `unlock_resources()`: Releases locks +- `release_locked_resources()`: Alias for unlock (for clarity in rejection paths) + +##### 3. `contracts/utility_contracts/src/settlement.rs` +Main settlement contract with: + +###### Constants +- `MIN_SETTLEMENT_WINDOW = 60` (1 minute) +- `MAX_SETTLEMENT_WINDOW = 604800` (7 days) + +###### Error Codes +- `DeadlineExceeded = 1`: Settlement deadline exceeded +- `InvalidSettlementWindow = 2`: Window outside valid range +- `ProposalNotFound = 3`: Proposal doesn't exist +- `AlreadyFinalized = 4`: Proposal already finalized +- `Unauthorized = 5`: Unauthorized access + +###### Functions + +**`propose_settlement()`** +- Validates settlement_window is in range [60, 604800] +- Requires payer authorization +- Calculates settlement_deadline = submission_timestamp + settlement_window +- Locks resources +- Stores proposal + +**`finalize_settlement()`** +- **CRITICAL**: First operation checks `contract.ledger().timestamp() > settlement_deadline` +- Hard deadline enforcement with 0 grace period +- Panic with `DeadlineExceeded` if deadline passed +- Releases resources before panicking (atomic rollback) +- Requires payee authorization +- Prevents double finalization + +**`get_proposal()`** +- Retrieves proposal by ID + +**`is_deadline_exceeded()`** +- Checks if a proposal's deadline has passed + +##### 4. Updated `contracts/utility_contracts/src/lib.rs` +Added module declarations: +```rust +pub mod settlement; +pub mod settlement_lock_manager; +pub mod settlement_types; +``` + +#### Implementation Details + +##### Deadline Enforcement +The deadline check is implemented as the **first operation** in `finalize_settlement()`: + +```rust +let current_timestamp = env.ledger().timestamp(); +if current_timestamp > proposal.settlement_deadline { + release_locked_resources(&env, &mut proposal, &token_address); + panic_with_error!(&env, SettlementError::DeadlineExceeded); +} +``` + +This ensures: +1. **Zero grace period** - strictly rejects if current_timestamp > deadline +2. **No state mutation before check** - happens before any other logic +3. **Atomic rollback** - releases locks before panicking (Soroban's panic reverts all state changes) + +##### Resource Locking +Resources are locked when a proposal is created and automatically released on: +- Successful finalization +- Deadline expiration (before panic) +- Any error condition + +Since Soroban's `panic_with_error!` reverts all state changes in the current transaction, the lock release call before panic ensures proper cleanup. + +##### Settlement Window Validation +The `propose_settlement()` function validates the window parameter: +```rust +if settlement_window < MIN_SETTLEMENT_WINDOW || settlement_window > MAX_SETTLEMENT_WINDOW { + panic_with_error!(env, SettlementError::InvalidSettlementWindow); +} +``` + +This enforces the required bounds of 60 seconds (1 minute) to 604800 seconds (7 days). + +#### Tests Included + +The implementation includes comprehensive tests: + +1. **`test_settlement_window_validation()`** + - Tests window < 60 seconds fails + - Tests window > 7 days fails + +2. **`test_settlement_finalized_before_deadline_succeeds()`** + - Settlement at timestamp 1200 with deadline 1300 succeeds + +3. **`test_settlement_finalized_exactly_at_deadline_succeeds()`** + - Settlement at exactly deadline timestamp succeeds + +4. **`test_settlement_finalized_after_deadline_fails()`** + - Settlement 1 second after deadline panics with DeadlineExceeded (error code 1) + +5. **`test_settlement_window_bounds()`** + - Tests minimum valid window (60 seconds) + - Tests maximum valid window (604800 seconds) + +6. **`test_is_deadline_exceeded()`** + - Tests deadline checking before and after expiry + +7. **`test_double_finalization_fails()`** + - Ensures proposals cannot be finalized twice + +#### Security Features + +1. **Hard Deadline**: No grace period, strictly enforces timestamp check +2. **Authorization**: Requires payer auth for proposal, payee auth for finalization +3. **Atomic Operations**: State reverts on any error via panic mechanism +4. **Resource Safety**: Locks released before panic to prevent resource leaks +5. **Front-running Protection**: Deadline enforcement prevents stale settlement execution + +#### Current Status + +##### ✅ Implemented +- Settlement proposal struct with all required fields +- Deadline calculation (submission_timestamp + settlement_window) +- Hard deadline enforcement in finalize_settlement() +- Settlement window bounds validation [60, 604800] +- Resource locking/unlocking mechanism +- All required error types +- Comprehensive test suite +- Module integration into lib.rs + +##### ⚠️ Note on Build Errors +The repository currently has 128 existing compilation errors in other parts of the codebase that are unrelated to the settlement feature. The settlement module itself is correctly implemented according to the specification. These existing errors need to be fixed separately before the entire project can compile. + +#### Next Steps + +To complete this feature: +1. Fix the 128 existing compilation errors in the main codebase +2. Run the settlement tests: `cargo test --package utility_contracts settlement` +3. Perform integration testing with the token contract for actual resource locking +4. Security audit of the deadline enforcement logic +5. Deploy to testnet and verify behavior + +#### Compliance with Requirements + +| Requirement | Status | Implementation | +|------------|--------|----------------| +| settlement_deadline field (u64) | ✅ | In SettlementProposal struct | +| Deadline check first operation | ✅ | First line in finalize_settlement() | +| Zero grace period | ✅ | Strict > comparison | +| Window range [60, 604800] | ✅ | Constants + validation | +| Atomic resource release | ✅ | release_locked_resources() before panic | +| ledger().timestamp() usage | ✅ | Used for deadline comparison | +| Max delay ≤ deadline - submission | ✅ | Enforced by timestamp check | +| Test cases (a)-(d) | ✅ | All implemented in mod test | + +All technical invariants and implementation blueprint requirements have been fulfilled. + + +## 5. Security Considerations + +### Source: `SECURITY.md` + +### Security Policy & Formal Verification Results + +#### Reporting a Vulnerability + +Please report security vulnerabilities by opening a **private** GitHub Security Advisory at: +`https://github.com/Utility-Protocol/Utility-contracts/security/advisories/new` + +Do **not** open a public issue for security-sensitive findings. + +--- + +#### Formal Proof: Per-Second Stream Exhaustion Invariant (Issue #254) + +##### Invariant Statement + +> **For every active stream:** +> `current_time ≤ start_time + ⌊initial_balance / flow_rate⌋` +> +> Equivalently, `calculate_remaining_balance(balance, rate, elapsed)` **never returns a negative value**. + +This invariant guarantees that the contract is **insolvent-proof** with respect to individual device streams: a stream can never pay for more seconds than its deposited balance allows. + +##### Mathematical Proof + +Let: +- `B` = initial balance (integer, stroops or token units) +- `R` = flow rate (integer, units per second, `R > 0`) +- `T_max` = `⌊B / R⌋` (maximum seconds the stream can run) +- `C(t)` = consumed at time `t` = `R × t` (integer multiplication) + +**Claim:** `B - C(T_max) ≥ 0` + +**Proof:** +``` +T_max = ⌊B / R⌋ +⟹ T_max ≤ B / R +⟹ R × T_max ≤ B (multiply both sides by R > 0) +⟹ B - R × T_max ≥ 0 (rearrange) +⟹ B - C(T_max) ≥ 0 ∎ +``` + +**Rounding direction:** All divisions use Rust integer truncation (rounds toward zero / floor for positive values), which always rounds **down in favour of the contract**. This means the contract never charges for a fractional second it has not earned. + +**Overflow protection:** All arithmetic uses `saturating_mul` and `saturating_sub`, which clamp to `i128::MAX` / `i128::MIN` rather than wrapping. The `max(0)` clamp in `calculate_remaining_balance` provides a final safety net. + +##### Fuzz Test Coverage + +The following tests in `contracts/utility_contracts/src/fuzz_tests.rs` verify the invariant: + +| Test | Description | Inputs | +|------|-------------|--------| +| `test_stream_exhaustion_invariant_randomised` | 100 000 randomised (balance, rate) pairs via deterministic LCG | balance ∈ [1, 10¹²], rate ∈ [1, 10⁶] | +| `test_stream_never_negative_after_pause_resume` | 10-year simulation with pause/resume and partial top-ups | Fixed scenario, 315 M seconds | +| `test_rounding_always_favours_solvency` | Verifies floor-division rounding direction | Hand-crafted edge cases | +| `test_calculate_remaining_balance_never_negative` | Grid search over (balance, rate, elapsed) | 6 × 5 × 5 = 150 combinations including extremes | + +All tests run on every Pull Request via the CI workflow (`.github/workflows/test.yml`). + +##### Scope of the Guarantee + +- ✅ Single-stream balance exhaustion +- ✅ Pause / resume cycles +- ✅ Partial top-ups mid-stream +- ✅ Rounding-error accumulation over 10-year durations +- ✅ Overflow / underflow protection via saturating arithmetic +- ⚠️ Multi-stream interactions (covered by integration tests, not this invariant) +- ⚠️ Oracle price conversion rounding (separate audit scope) + +##### Auditor Notes + +The formal invariant proof above satisfies the **"High Assurance"** requirement for institutional auditors. The deterministic fuzz harness (`test_stream_exhaustion_invariant_randomised`) can be reproduced exactly by any auditor by running: + +```bash +cargo test -p utility_contracts test_stream_exhaustion_invariant_randomised -- --nocapture +``` + +--- + +#### Other Security Properties + +##### Auto-Rent-Deduction (Issue #258) + +- Rent is only deducted when the contract TTL falls below a 6-month safety threshold (~3 110 400 ledgers). +- Deduction is capped at 1 000 stroops (0.0001 XLM) per claim. +- For non-XLM tokens the deduction is skipped silently to avoid blocking the stream. +- A `RentRenew` event is emitted with the deduction amount and new TTL for auditability. + +##### Multi-Sig Technical Veto (Issue #253) + +- Fleet-level configuration changes require a 48-hour staging window. +- The Fleet Security Council (3-of-5 multi-sig) can veto any staged update within the window. +- Emergency circuit-breaker updates bypass the staging window. +- Lost council keys can be rotated by the DAO after a 7-day delay. +- All staged and vetoed events are emitted on-ledger for public transparency. + +##### Carbon-Credit Streaming (Issue #252) + +- The green energy ratio and credit multiplier must be set by the provider (acting as the whitelisted environmental auditor). +- Credits accumulate as fractional slices; only full integer credits trigger a cross-contract mint. +- If the minting contract is paused or has hit its issuance cap, pending credits are stored in a `Deferred_Issuance` buffer and can be retried later. +- No fractional "dust" is lost: every stroop of green usage is counted in the accumulator. + + +### Source: `contracts/docs/security/macro-auth.md` + +### Mint/Burn Authorization — Model & Safety Invariants + +Issue #4 — "Custom Validation Macro Override in Resource Tokenization +Authorization" + +#### Scope correction + +The issue describes a procedural macro `#[requires_role(Role::Minter)]` whose +expansion skips the authorization check when an `#[allow(unused)]` attribute is +present, allowing unauthorized minting. + +**No such macro exists in this codebase.** There is no `#[requires_role]` +attribute, no `Role` enum, and no `role_check` proc-macro crate. Authorization is +not attribute-driven, so the described macro-expansion bypass is not applicable. + +This document records the **actual** authorization model, the invariant it +upholds, and the tests that now lock it in. + +#### Actual authorization model (`resource-token`) + +``` +mint(env, to, amount) -> authorize_mint(env) -> authorize_with_chain(env) +burn(env, from, amount) -> authorize_burn(env) -> authorize_with_chain(env) + +authorize_with_chain(env): + admin = get_admin(env) // panics "NoAdmin" if unset + admin.require_auth() // panics if the admin has not authorized +``` + +`authorize_*` is the **first statement** of `mint`/`burn`, before any state is +read or written. The gate is `Address::require_auth()`, which the Soroban host +validates against the actual authorization context — it cannot be spoofed by an +intermediate contract in the call chain. There is no code path that reaches the +balance/supply mutation without passing `admin.require_auth()`. + +##### Invariant + +``` +∀ mint/burn operation: the admin has authorized the operation (require_auth) +``` + +#### Why this could regress silently (the real gap the issue points at) + +Every pre-existing test uses `env.mock_all_auths()`, which auto-approves **all** +authorization. Under that mode the gate is never exercised: a regression that +deleted `authorize_mint()`/`authorize_burn()` from `mint`/`burn` would still pass +the entire suite. + +##### Tests added + +`contracts/resource-token/src/test.rs`: + +- `test_mint_rejected_without_authorization` — after setup, drop all auth with + `env.set_auths(&[])`; `mint` must panic. +- `test_burn_rejected_without_authorization` — same, for `burn`. +- `test_mint_rejected_without_auth_leaves_state_unchanged` — `try_mint` returns + `Err` and neither balance nor total supply changes. + +These exercise the gate with an empty authorization set, so removing or bypassing +the authorization call now fails the suite. + +#### Known limitation / recommended follow-up (out of scope here) + +`authorize_with_chain` only honors the **admin**. The contract also exposes +`authorize_operator` / `is_valid_operator` and documents that "the admin or a +valid operator can mint", but the mint/burn path **never checks operators** — an +operator cannot actually mint, because `require_auth()` is only ever called on +the admin's address. + +Making operator-delegated minting work securely requires threading the caller's +`Address` into `mint`/`burn` (so the contract can `require_auth()` the operator +and verify it is currently valid). That is a **breaking signature change** and is +intentionally left as a separate change; the misleading documentation should be +corrected at the same time. + +The current behaviour is **safe** (only the admin can mint/burn); the limitation +is reduced functionality, not an authorization bypass. +``` + + +### Source: `AUDIT_READY_RUNBOOK.md` + +### Audit-Ready Runbook — Utility-Protocol Contracts + +**Contract ID (Testnet):** `CB7PSJZALNWNX7NLOAM6LOEL4OJZMFPQZJMIYO522ZSACYWXTZIDEDSS` +**Network:** Stellar Testnet — replace `--network testnet` with `--network mainnet` for production +**Last updated:** 2026-04-28 +**Classification:** CONFIDENTIAL — DAO Core Team Only +**Audit Status:** ✅ Ready for Zealynx Security Audit + +--- + +#### Table of Contents + +1. [Executive Summary](#1-executive-summary) +2. [Security Architecture Overview](#2-security-architecture-overview) +3. [Roles and Responsibilities](#3-roles-and-responsibilities) +4. [Pre-Incident Checklist](#4-pre-incident-checklist) +5. [Scenario A — Active Exploit / Hack in Progress](#5-scenario-a--active-exploit--hack-in-progress) +6. [Scenario B — Protocol Pause (Planned or Precautionary)](#6-scenario-b--protocol-pause-planned-or-precautionary) +7. [Scenario C — Wasm Hash Upgrade](#7-scenario-c--wasm-hash-upgrade) +8. [Scenario D — Migrating Trapped State](#8-scenario-d--migrating-trapped-state) +9. [Scenario E — Multi-Sig Withdrawal Freeze](#9-scenario-e--multi-sig-withdrawal-freeze) +10. [Scenario F — Legal Freeze](#10-scenario-f--legal-freeze) +11. [Scenario G — Gas Buffer Exhaustion](#11-scenario-g--gas-buffer-exhaustion) +12. [Scenario H — Admin Key Compromise](#12-scenario-h--admin-key-compromise) +13. [Scenario I — Oracle Failure](#13-scenario-i--oracle-failure) +14. [Scenario J — Velocity Limit Breach / Flash Drain](#14-scenario-j--velocity-limit-breach--flash-drain) +15. [Scenario K — Nonce Desync Attack (New)](#15-scenario-k--nonce-desync-attack-new) +16. [Scenario L — Tariff Oracle Compromise (New)](#16-scenario-l--tariff-oracle-compromise-new) +17. [Scenario M — Ghost Stream Cleanup (New)](#17-scenario-m--ghost-stream-cleanup-new) +18. [Post-Incident Procedures](#18-post-incident-procedures) +19. [Multi-Sig Signer Reference Card](#19-multi-sig-signer-reference-card) +20. [Contact Tree](#20-contact-tree) +21. [Audit Checklist](#21-audit-checklist) + +--- + +#### 1. Executive Summary + +The Utility-Protocol Contracts platform provides a decentralized utility streaming protocol with comprehensive security measures including: + +- **Tamper-proof nonce synchronization** for IoT device liveness verification +- **Time-of-Use tariff pricing** with 24-hour schedules +- **Automated ghost stream cleanup** to maintain ledger efficiency +- **Multi-sig governance** for critical operations +- **Emergency response capabilities** for rapid threat mitigation + +##### Security Improvements Implemented (Issues #260-263) + +| Issue | Feature | Security Benefit | +|-------|---------|------------------| +| #260 | Hardware Nonce Sync | Eliminates replay attacks against device liveness monitoring | +| #261 | Utility-Tariff Oracle | Enables complex pricing models with seamless rate transitions | +| #262 | Ghost Stream Sweeper | Reduces ledger footprint while maintaining historical integrity | +| #263 | Documentation Sweep | Enterprise-grade documentation for audit readiness | +| #68 | Kafka Lag Monitor & Scaler | System-wide real-time Kafka consumer lag monitoring and auto-scaling | + +--- + +#### 2. Security Architecture Overview + +##### 2.1 Core Security Components + +###### Nonce Synchronization System +- **Purpose:** Prevent replay attacks on IoT device heartbeats +- **Implementation:** Strict incrementing u64 nonce per device MAC address +- **Security Features:** + - +1 to +5 nonce window for network jitter tolerance + - Multi-sig nonce reset for compromised devices + - Automatic suspicious device marking + - Comprehensive audit trail + +###### Tariff Oracle System +- **Purpose:** Manage Time-of-Use pricing schedules +- **Implementation:** 24-hour pricing windows with grid administrator control +- **Security Features:** + - 24-hour notice period for tariff changes + - Cryptographic signature verification + - Temporary storage optimization + - Seamless rate interpolation + +###### Ghost Stream Management +- **Purpose:** Maintain ledger efficiency by pruning abandoned streams +- **Implementation:** 90-day zero balance threshold with archive preservation +- **Security Features:** + - Cryptographic archive hashes for integrity + - Gas bounty incentives for relayers + - Protection for streams with pending buffers + - Historical audit trail preservation + +##### 2.2 Threat Model Coverage + +| Threat Vector | Mitigation | Implementation | +|--------------|------------|----------------| +| Replay Attacks | Nonce synchronization | Issue #260 | +| Price Manipulation | Signed tariff updates | Issue #261 | +| Ledger Bloat | Automated cleanup | Issue #262 | +| Insider Threats | Multi-sig controls | Existing | +| Smart Contract Bugs | Comprehensive testing | Issue #263 | + +--- + +#### 3. Roles and Responsibilities + +| Role | On-chain Key / Storage | Duty | New Security Features | +|---|---|---|---| +| **DAO Admin** | `DataKey::CurrentAdmin` | Propose/finalize Wasm upgrades, set compliance officer, grant provider verification, set velocity limits | Tariff oracle admin, Nonce reset authorization | +| **Compliance Officer** | `DataKey::ComplianceOfficer` | Trigger and release legal freezes | Ghost stream emergency cleanup | +| **Finance Wallet (×3–5)** | `MultiSigConfig.finance_wallets` | Propose, approve, revoke, and cancel large withdrawal requests; quorum = `required_signatures` | Ghost stream gas bounty approval | +| **Oracle / Resolver** | `DataKey::Oracle` | Resolve service challenges (`resolve_challenge`) | Tariff oracle signing | +| **Grid Administrator** | `DataKey::TariffOracleAdmin` | Manage tariff schedules | **New** - Issue #261 | +| **Nonce Reset Authority** | `DataKey::AuthorizedNonceResetters` | Reset compromised device nonces | **New** - Issue #260 | +| **Provider** | Per-meter `provider` field | Pause/shutdown individual meters, initiate firmware updates, manage gas buffer | Device nonce management | +| **Ghost Sweeper** | Decentralized relayer | Prune abandoned streams | **New** - Issue #262 | +| **Compliance Council** | Off-chain multi-sig (≥2) | Release legal freezes | Emergency tariff overrides | + +##### Multi-sig quorum rule + +Any action requiring `required_signatures` approvals **must be coordinated off-chain first** (Signal group, emergency Telegram, or PagerDuty). Confirm quorum is available before submitting the first on-chain transaction. The contract enforces the threshold — a request with insufficient approvals will revert on execution. + +##### Key storage locations (for incident verification) + +``` +DataKey::CurrentAdmin → DAO Admin address +DataKey::ComplianceOfficer → Compliance Officer address +DataKey::Oracle → Oracle/Resolver address +DataKey::TariffOracleAdmin → Grid Administrator address (New) +DataKey::MultiSigConfig(addr) → Per-provider multi-sig config +DataKey::VetoDeadline → Active upgrade veto deadline (Unix timestamp) +DataKey::ProposedUpgrade → Active UpgradeProposal struct +DataKey::DeviceNonce(mac) → Device nonce state (New) +DataKey::CurrentTariffSchedule → Active tariff schedule (New) +DataKey::StreamArchive(id) → Pruned stream archive (New) +``` + +--- + +#### 4. Pre-Incident Checklist + +Run every check before executing any emergency command. Do not skip steps. + +```bash +### 1. Confirm Stellar CLI is installed and on PATH +stellar --version + +### 2. Confirm you are targeting the correct network +stellar network ls + +### 3. Export the contract address +export CONTRACT=CB7PSJZALNWNX7NLOAM6LOEL4OJZMFPQZJMIYO522ZSACYWXTZIDEDSS + +### 4. Export signing identities for your role +export ADMIN_KEY= +export PROVIDER_KEY= +export FINANCE_KEY= +export GRID_ADMIN_KEY= + +### 5. Verify the contract is responsive +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_count + +### 6. Check the current meter count and note it +export METER_COUNT=$(stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_count) +echo "Total meters: $METER_COUNT" + +### 7. Verify your key matches the expected admin address +stellar keys address $ADMIN_KEY +### Compare output against the address stored in DataKey::CurrentAdmin + +### 8. Check nonce sync system health +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + nonce_sync_health_check + +### 9. Verify tariff oracle configuration +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_tariff_oracle_admin + +### 10. Check ghost stream statistics +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_sweeper_statistics + +### 11. Check block explorer for any anomalous recent transactions +### https://stellar.expert/explorer/testnet/contract/$CONTRACT +``` + +> **If the contract is unresponsive:** The Stellar network may be congested or the contract TTL may have expired. Check https://status.stellar.org and the block explorer before proceeding. + +--- + +#### 5. Scenario A — Active Exploit / Hack in Progress + +##### Immediate Actions (Execute in Order) + +1. **FREEZE ALL STREAMS** (DAO Admin only) +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $ADMIN_KEY \ + -- \ + emergency_freeze_all_streams +``` + +2. **PAUSE NONCE VERIFICATION** (Grid Admin only) +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $GRID_ADMIN_KEY \ + -- \ + pause_nonce_verification +``` + +3. **LOCK TARIFF ORACLE** (Grid Admin only) +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $GRID_ADMIN_KEY \ + -- \ + emergency_lock_tariff_oracle +``` + +4. **ENABLE ENHANCED MONITORING** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $ADMIN_KEY \ + -- \ + enable_emergency_monitoring +``` + +##### Verification Steps +```bash +### Confirm all streams are frozen +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + are_streams_frozen + +### Check nonce verification status +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + is_nonce_verification_active + +### Verify tariff oracle is locked +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + is_tariff_oracle_locked +``` + +--- + +#### 15. Scenario K — Nonce Desync Attack (New) + +##### Detection Indicators +- Multiple `NonceDesyncAlert` events in short succession +- Devices marked as suspicious +- Replay attack patterns in event logs + +##### Response Procedures + +1. **Investigate Attack Pattern** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_nonce_desync_alerts \ + --limit 50 +``` + +2. **Isolate Compromised Devices** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $PROVIDER_KEY \ + -- \ + quarantine_devices_by_mac \ + --mac-list +``` + +3. **Reset Device Nonces** (Multi-sig required) +```bash +### Step 1: Create reset request +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $AUTHORIZED_RESETTER_KEY \ + -- \ + create_nonce_reset_request \ + --meter-id \ + --device-mac \ + --new-nonce 0 + +### Step 2: Get approvals from other authorized resetters +### (Repeat for each required signature) +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $OTHER_RESETTER_KEY \ + -- \ + approve_nonce_reset \ + --proposal-id + +### Step 3: Execute reset (final approver) +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $FINAL_RESETTER_KEY \ + -- \ + execute_nonce_reset \ + --proposal-id +``` + +4. **Update Security Parameters** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $ADMIN_KEY \ + -- \ + update_nonce_security_params \ + --window-size 3 \ + --suspicious-threshold 5 +``` + +--- + +#### 16. Scenario L — Tariff Oracle Compromise (New) + +##### Detection Indicators +- Invalid tariff rates being applied +- Unauthorized tariff schedule updates +- Grid administrator key compromise + +##### Response Procedures + +1. **Immediate Oracle Lockdown** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $ADMIN_KEY \ + -- \ + emergency_lock_tariff_oracle +``` + +2. **Revert to Default Schedule** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $ADMIN_KEY \ + -- \ + revert_to_default_tariff_schedule +``` + +3. **Replace Grid Administrator** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $ADMIN_KEY \ + -- \ + set_tariff_oracle_admin \ + --new-admin +``` + +4. **Audit Recent Tariff Changes** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_tariff_update_history \ + --days 7 +``` + +--- + +#### 17. Scenario M — Ghost Stream Cleanup (New) + +##### Detection Indicators +- High storage usage on contract +- Many streams with zero balance > 90 days +- Performance degradation + +##### Response Procedures + +1. **Assess Cleanup Candidates** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_ghost_stream_candidates \ + --limit 100 +``` + +2. **Authorize Batch Cleanup** (Multi-sig if needed) +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $RELAYER_KEY \ + -- \ + batch_prune_ghost_streams \ + --stream-ids \ + --relayer +``` + +3. **Verify Cleanup Results** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_sweeper_statistics +``` + +4. **Check Archive Integrity** +```bash +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + verify_archive_integrity \ + --stream-id +``` + +--- + +#### 17.1 Scenario N — Kafka Consumer Group Lag Spike (New) + +##### Detection Indicators +- `Group Lag Alert` (Warning/Critical) triggered in the System Monitoring Suite. +- Large backlog of unprocessed telemetry, delayed settlements, or pending billing updates. + +##### Response Procedures +1. **Analyze Lag on Dashboard:** Inspect the **Kafka Lag & Auto-Scaler** tab to identify bottlenecked partitions. +2. **Override Settings:** Securely lift the `MAX_CONSUMERS` ceiling to scale capacity immediately: +```bash +stellar contract invoke --id $CONTRACT --network testnet --source $ADMIN_KEY -- override_kafka_scaler_config --max_consumers 16 --target_lag_per_consumer 250 +``` +3. **Verify Rebalance:** Confirm the `REBALANCE` completes within the 3-second penalty window using the audit logs. + +#### 17.2 Scenario O — Kafka Auto-Scaling Actuator Failure (New) + +##### Detection Indicators +- `LIMIT_REACHED` logged in event stream with high lag. +- Actuator endpoint reports credential expiration or `503 Service Unavailable`. + +##### Response Procedures +1. **Manual Scale Actuation:** Force scaling via docker/kubernetes direct provisioning to handle backlogs. +2. **Prioritize Topics:** Suspend green-grant topics to give maximum CPU resources to the main prepaid billing queue. + +--- + +#### 18. Post-Incident Procedures + +##### 1. Incident Documentation +- Create detailed incident report +- Document all actions taken +- Preserve event logs and signatures +- Update runbook with lessons learned + +##### 2. Security Review +- Conduct root cause analysis +- Review all affected systems +- Update threat model +- Implement additional safeguards + +##### 3. Communication +- Notify all stakeholders +- Publish post-mortem (if appropriate) +- Update documentation +- Schedule security review meeting + +##### 4. System Recovery +- Gradually restore services +- Monitor for anomalies +- Update monitoring thresholds +- Conduct penetration testing + +--- + +#### 19. Multi-Sig Signer Reference Card + +##### Grid Administrator (Tariff Oracle) +```bash +### View current admin +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_tariff_oracle_admin + +### Update tariff schedule +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $GRID_ADMIN_KEY \ + -- \ + propose_tariff_update \ + --schedule \ + --signature +``` + +##### Nonce Reset Authority +```bash +### View authorized resetters +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + -- \ + get_authorized_nonce_resetters + +### Reset device nonce +stellar contract invoke \ + --id $CONTRACT \ + --network testnet \ + --source $RESETTER_KEY \ + -- \ + reset_device_nonce \ + --meter-id \ + --device-mac \ + --new-nonce +``` + +--- + +#### 20. Contact Tree + +``` +Level 1 (Immediate): DAO Admin, Compliance Officer +Level 2 (15 mins): Grid Administrator, Finance Wallets +Level 3 (30 mins): All Providers, Security Team +Level 4 (1 hour): Community, Public Relations +``` + +**Emergency Channels:** +- Signal Group: `utility-protocol-emergency` +- Telegram: `@iotbilling_emergency` +- PagerDuty: `utility-protocol-security` + +--- + +#### 21. Audit Checklist + +##### ✅ Documentation Requirements +- [ ] All public functions have comprehensive doc-comments +- [ ] All arguments and return values documented +- [ ] All authorized roles explicitly documented +- [ ] Cross-links between modules are perfect +- [ ] No TODO or FIXME comments remain +- [ ] Security considerations documented +- [ ] Error codes and handling documented + +##### ✅ Code Quality Standards +- [ ] No hardcoded secrets or credentials +- [ ] All external dependencies audited +- [ ] Input validation on all public functions +- [ ] Proper access control mechanisms +- [ ] Comprehensive test coverage +- [ ] Fuzz testing for critical components +- [ ] Gas optimization where appropriate + +##### ✅ Security Verification +- [ ] Replay attack protection implemented +- [ ] Rate limiting and velocity controls +- [ ] Multi-sig requirements for critical operations +- [ ] Emergency pause mechanisms +- [ ] Audit trail preservation +- [ ] Cryptographic integrity verification +- [ ] Key compromise procedures + +##### ✅ Operational Readiness +- [ ] Monitoring and alerting configured +- [ ] Backup and recovery procedures +- [ ] Incident response runbook tested +- [ ] Key rotation procedures documented +- [ ] Upgrade and migration procedures +- [ ] Performance benchmarks established + +--- + +#### Conclusion + +This runbook provides comprehensive procedures for managing the Utility-Protocol Contracts platform with the new security improvements implemented in Issues #260-263. The platform is now audit-ready with enterprise-grade documentation, comprehensive security measures, and operational procedures that meet the highest standards for decentralized utility management. + +**Next Steps:** +1. Schedule external security audit with Zealynx +2. Conduct penetration testing on new features +3. Perform full-system integration testing +4. Execute mainnet deployment checklist + +--- + +*This document is confidential and intended for authorized personnel only. Do not distribute outside the DAO core team without explicit permission.* + + diff --git a/README.md b/README.md deleted file mode 100644 index 0fb6cb0..0000000 --- a/README.md +++ /dev/null @@ -1,191 +0,0 @@ -# Utility-Protocol Contracts - -Soroban smart contracts for a decentralized utility metering and streaming protocol on Stellar. Supports prepaid/postpaid billing, continuous streaming, variable-rate tariffs, gas buffers, ZK-SNARK sensor privacy, multi-sig governance, and emergency response. - -## Features - -- **Utility Metering** — Track energy/water consumption with precision billing -- **Prepaid & Postpaid Billing** — Both models supported -- **Continuous Streaming** — Real-time balance monitoring with buffer protection -- **Variable Rate Tariffs** — Peak/off-peak pricing (18:00–21:00 UTC at 1.5× rate) -- **Gas Buffer** — Pre-paid XLM buffer ensures withdrawals clear during network congestion -- **ZK-SNARK Privacy** — Groth16 proofs let meters prove usage without revealing raw readings -- **Firmware Update Gate** — Time-limited, cryptographically signed update authorization -- **Multi-Sig Governance** — 3-of-5 finance wallet quorum for large withdrawals -- **Emergency Response** — Circuit breakers, legal freezes, velocity limits, protocol pauses -- **Dust Sweeper** — Prunes fractional remainders from depleted streams -- **Grant Stream** — Conservation goals trigger automatic grant matching -- **Scheduled Backup Verification** — Restore-tested database backups with metrics, alerts, and canary rollout guidance -- **Oracle Aggregation Framework** — Multi-provider oracle aggregation with a Chainlink `AggregatorV3Interface` adapter, median consensus, deviation/staleness validation, graceful fallback, and per-provider health monitoring (`contracts/oracle-aggregator`) - -## Project Structure - -``` -Utility-contracts/ -├── contracts/ -│ ├── Cargo.toml # Workspace root -│ ├── utility_contracts/ # Main contract -│ │ ├── src/lib.rs # Core implementation -│ │ ├── src/test.rs # Test suite -│ │ └── Cargo.toml -│ └── price_oracle/ # Price oracle contract -├── webhook-delivery-service/ # High-performance off-chain Webhook service with retry & SSRF shielding (TS) -├── meter-simulator/ # Device simulator (JS) -├── usage-dashboard/ # Real-time Next.js analytics & Webhook monitor dashboard -├── docs/ # Architecture, deployment and operational runbooks -├── examples/ # Usage examples -├── scripts/ # Deployment scripts -├── .github/workflows/ci.yml # CI pipeline -├── SECURITY.md # Security policy & formal proofs -├── CONTRIBUTING.md # Contribution guidelines -└── EMERGENCY_RUNBOOK.md # Emergency procedures -``` - -### Webhook Delivery Service - -An enterprise-grade, high-performance off-chain delivery daemon for real-time Soroban alerts (e.g. `LowBalanceAlert`, device tampers). -- **Performance**: `< 100ms` P99 ingestion latency target via an asynchronous event-driven memory queue. -- **Robust Security**: Includes HMAC-SHA256 and Ed25519 signature headers, strict replay protection windowing, and thorough SSRF IP/DNS blacklisting. -- **Resiliency**: Built-in exponential backoff retry schedules with full randomized jitter to survive downstream subscriber downtimes and network drops. -- **Operational Guides**: See [WEBHOOK_ARCHITECTURE.md](docs/WEBHOOK_ARCHITECTURE.md), [WEBHOOK_DEPLOYMENT.md](docs/WEBHOOK_DEPLOYMENT.md), and [WEBHOOK_RUNBOOK.md](docs/WEBHOOK_RUNBOOK.md). - -## Architecture - -### Variable Rate Tariffs - -Peak hours: **18:00–21:00 UTC** (1.5× off-peak rate). - -``` -Peak rate = off_peak_rate × 3 / 2 - -Example: off_peak = 10 tokens/sec - peak = 15 tokens/sec -``` - -| UTC Hour | Seconds | Status | -|----------|---------|--------| -| 00:00 | 0 | OFF-PEAK | -| 12:00 | 43,200 | OFF-PEAK | -| 18:00 | 64,800 | PEAK | -| 20:59 | 75,599 | PEAK | -| 21:00 | 75,600 | OFF-PEAK | - - -### Observability - -The meter simulator propagates W3C Trace Context metadata in MQTT usage and heartbeat payloads, and the dashboard includes trace health indicators for the 100 ms P99 critical-path target. See [Distributed Tracing and Trace Context Propagation](docs/DISTRIBUTED_TRACING.md) for architecture, rollout, alerting, security review, and runbook guidance. - -### Gas Buffer - -Ensures 100% service availability during network congestion. - -| Constant | Value | Description | -|----------|-------|-------------| -| `MIN_GAS_BUFFER` | 100 XLM | Minimum required buffer | -| `MAX_GAS_BUFFER` | 10,000 XLM | Maximum buffer capacity | -| `GAS_BUFFER_TOP_UP_THRESHOLD` | 200 XLM | Auto top-up trigger | - -### Firmware Update Authorization Gate - -Provider-initiated, device-completed firmware updates with Ed25519 signature verification and a 2-hour maximum window. - -### Stream Balance Invariant (Formal Proof) - -> For every active stream: `current_time ≤ start_time + ⌊initial_balance / flow_rate⌋` - -Verified via 15 property tests with 100+ randomized cases each, covering pause/resume cycles, rounding direction, and overflow protection. - - -### Chaos Engineering in Staging - -Staging resilience exercises are governed by the [Chaos Engineering Testing Blueprint](docs/runbooks/chaos-engineering-staging.md). The blueprint defines approved fault scenarios, security guardrails, P99 and availability SLOs, monitoring requirements, and blue-green/canary rollout steps for chaos-enabled staging deployments. - -### Security Properties - -- **Nonce sync** prevents replay attacks on IoT heartbeats -- **Multi-sig veto** for fleet-level config changes (48h staging window) -- **Carbon-credit streaming** with fractional accumulator and deferred minting -- **Auto-rent deduction** capped at 1,000 stroops per claim - -## Deployment - -- **Network:** Stellar Testnet -- **Contract ID:** `CB7PSJZALNWNX7NLOAM6LOEL4OJZMFPQZJMIYO522ZSACYWXTZIDEDSS` - -## Development - -### One-command local onboarding - -Run the repository onboarding script before your first local build. It validates Git, ripgrep, Rust/Cargo, rustup, the WASM target, Node.js, and npm; installs npm dependencies for the JavaScript workspaces unless skipped; and prints the recommended validation commands. - -```bash -./scripts/onboard.sh - -# Validate prerequisites without installing dependencies -./scripts/onboard.sh --check-only -``` - -### Manual build and test commands - -```bash -# Build -cd contracts && cargo build --target wasm32-unknown-unknown --release - -# Test -cargo test - -# Coverage (requires cargo-llvm-cov) -COVERAGE_THRESHOLD=80 scripts/coverage.sh - -# Lint -cargo fmt --all -- --check -cargo clippy --all-targets --all-features -- -D warnings -``` - -## CI/CD Pipeline - -The GitHub Actions workflow (`.github/workflows/ci.yml`) automatically runs on: -- **Push to main branch** - Ensures main branch is always tested -- **Pull Requests to main** - Prevents breaking changes from being merged - -### Dependency Vulnerability Scanning - -A dedicated GitHub Actions workflow (`.github/workflows/dependency-vulnerability-scan.yml`) runs on pull requests, pushes to `main`, a daily schedule, and manual dispatch. It blocks vulnerable dependency changes with GitHub Dependency Review, audits Rust lockfiles with `cargo audit`, audits Node.js projects with `npm audit`, and publishes a workflow summary for security review. See `docs/runbooks/DEPENDENCY_VULNERABILITY_SCANNING.md` for triage, monitoring, and rollout procedures. - -### Testing Stages - -1. **Environment Setup**: Rust toolchain with WASM target, Stellar CLI v25.1.0, dependency caching -2. **Code Quality**: `cargo fmt --all -- --check` + `cargo clippy --target wasm32-unknown-unknown -- -D warnings` -3. **Build**: `cargo build --target wasm32-unknown-unknown --release` -4. **Unit Tests**: `cargo test` including fuzz tests -5. **Coverage Gate**: `scripts/coverage.sh` enforces the configured line coverage threshold (`COVERAGE_THRESHOLD`, default 80%) for both the root package and contracts workspace -6. **Fuzz Tests**: Auto-detection and validation of fuzz infrastructure - -### Local Development - -```bash -cargo fmt --all -- --check -cargo clippy --target wasm32-unknown-unknown -- -D warnings -cargo build --target wasm32-unknown-unknown --release -cargo test -COVERAGE_THRESHOLD=80 scripts/coverage.sh -``` - -## ZK-SNARK Circuits for Sensor Privacy - -Hardware devices (meters) prove consumed energy/water amounts without revealing raw sensor readings using Groth16 proofs. - -**Circuit (Circom):** -- **Private inputs**: `usage_raw`, `salt`, `last_usage` -- **Public inputs**: `units_consumed`, `is_peak_hour`, `nullifier`, `commitment` -- **Constraints**: Integrity, range proof, commitment hash (Poseidon), nullifier uniqueness - -**Flow**: Device generates proof → submits via `submit_zk_usage_report` → contract verifies with BN254 host functions (`pairing_check`, `g1_add`, `g1_mul`) → nullifier checked → balance deducted. - -**Optimization**: Pre-computed verification key components, optimized host functions for EC ops, no big-integer WASM arithmetic. - -See [EMERGENCY_RUNBOOK.md](EMERGENCY_RUNBOOK.md) for operational procedures and [SECURITY.md](SECURITY.md) for formal verification results. - -## License - -By contributing, you agree that your contributions will be licensed under the same license as the project. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 4415bc5..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,101 +0,0 @@ -# Security Policy & Formal Verification Results - -## Reporting a Vulnerability - -Please report security vulnerabilities by opening a **private** GitHub Security Advisory at: -`https://github.com/Utility-Protocol/Utility-contracts/security/advisories/new` - -Do **not** open a public issue for security-sensitive findings. - ---- - -## Formal Proof: Per-Second Stream Exhaustion Invariant (Issue #254) - -### Invariant Statement - -> **For every active stream:** -> `current_time ≤ start_time + ⌊initial_balance / flow_rate⌋` -> -> Equivalently, `calculate_remaining_balance(balance, rate, elapsed)` **never returns a negative value**. - -This invariant guarantees that the contract is **insolvent-proof** with respect to individual device streams: a stream can never pay for more seconds than its deposited balance allows. - -### Mathematical Proof - -Let: -- `B` = initial balance (integer, stroops or token units) -- `R` = flow rate (integer, units per second, `R > 0`) -- `T_max` = `⌊B / R⌋` (maximum seconds the stream can run) -- `C(t)` = consumed at time `t` = `R × t` (integer multiplication) - -**Claim:** `B - C(T_max) ≥ 0` - -**Proof:** -``` -T_max = ⌊B / R⌋ -⟹ T_max ≤ B / R -⟹ R × T_max ≤ B (multiply both sides by R > 0) -⟹ B - R × T_max ≥ 0 (rearrange) -⟹ B - C(T_max) ≥ 0 ∎ -``` - -**Rounding direction:** All divisions use Rust integer truncation (rounds toward zero / floor for positive values), which always rounds **down in favour of the contract**. This means the contract never charges for a fractional second it has not earned. - -**Overflow protection:** All arithmetic uses `saturating_mul` and `saturating_sub`, which clamp to `i128::MAX` / `i128::MIN` rather than wrapping. The `max(0)` clamp in `calculate_remaining_balance` provides a final safety net. - -### Fuzz Test Coverage - -The following tests in `contracts/utility_contracts/src/fuzz_tests.rs` verify the invariant: - -| Test | Description | Inputs | -|------|-------------|--------| -| `test_stream_exhaustion_invariant_randomised` | 100 000 randomised (balance, rate) pairs via deterministic LCG | balance ∈ [1, 10¹²], rate ∈ [1, 10⁶] | -| `test_stream_never_negative_after_pause_resume` | 10-year simulation with pause/resume and partial top-ups | Fixed scenario, 315 M seconds | -| `test_rounding_always_favours_solvency` | Verifies floor-division rounding direction | Hand-crafted edge cases | -| `test_calculate_remaining_balance_never_negative` | Grid search over (balance, rate, elapsed) | 6 × 5 × 5 = 150 combinations including extremes | - -All tests run on every Pull Request via the CI workflow (`.github/workflows/test.yml`). - -### Scope of the Guarantee - -- ✅ Single-stream balance exhaustion -- ✅ Pause / resume cycles -- ✅ Partial top-ups mid-stream -- ✅ Rounding-error accumulation over 10-year durations -- ✅ Overflow / underflow protection via saturating arithmetic -- ⚠️ Multi-stream interactions (covered by integration tests, not this invariant) -- ⚠️ Oracle price conversion rounding (separate audit scope) - -### Auditor Notes - -The formal invariant proof above satisfies the **"High Assurance"** requirement for institutional auditors. The deterministic fuzz harness (`test_stream_exhaustion_invariant_randomised`) can be reproduced exactly by any auditor by running: - -```bash -cargo test -p utility_contracts test_stream_exhaustion_invariant_randomised -- --nocapture -``` - ---- - -## Other Security Properties - -### Auto-Rent-Deduction (Issue #258) - -- Rent is only deducted when the contract TTL falls below a 6-month safety threshold (~3 110 400 ledgers). -- Deduction is capped at 1 000 stroops (0.0001 XLM) per claim. -- For non-XLM tokens the deduction is skipped silently to avoid blocking the stream. -- A `RentRenew` event is emitted with the deduction amount and new TTL for auditability. - -### Multi-Sig Technical Veto (Issue #253) - -- Fleet-level configuration changes require a 48-hour staging window. -- The Fleet Security Council (3-of-5 multi-sig) can veto any staged update within the window. -- Emergency circuit-breaker updates bypass the staging window. -- Lost council keys can be rotated by the DAO after a 7-day delay. -- All staged and vetoed events are emitted on-ledger for public transparency. - -### Carbon-Credit Streaming (Issue #252) - -- The green energy ratio and credit multiplier must be set by the provider (acting as the whitelisted environmental auditor). -- Credits accumulate as fractional slices; only full integer credits trigger a cross-contract mint. -- If the minting contract is paused or has hit its issuance cap, pending credits are stored in a `Deferred_Issuance` buffer and can be retried later. -- No fractional "dust" is lost: every stroop of green usage is counted in the accumulator. diff --git a/SETTLEMENT_IMPLEMENTATION.md b/SETTLEMENT_IMPLEMENTATION.md deleted file mode 100644 index ee4d618..0000000 --- a/SETTLEMENT_IMPLEMENTATION.md +++ /dev/null @@ -1,179 +0,0 @@ -# Settlement Contract Implementation - -## Overview -This document describes the implementation of the settlement deadline enforcement feature as specified in the requirements. - -## Files Created - -### 1. `contracts/utility_contracts/src/settlement_types.rs` -Contains the `SettlementProposal` struct with all required fields: -- `proposal_id`: Unique identifier -- `payer`: Address of the payer -- `payee`: Address of the payee -- `amount`: Amount to be settled -- `rate`: Exchange rate at proposal time -- `submission_timestamp`: u64 epoch seconds when submitted -- `settlement_deadline`: u64 epoch seconds deadline (submission_timestamp + settlement_window) -- `finalized`: Boolean flag -- `resources_locked`: Boolean flag for resource locking - -### 2. `contracts/utility_contracts/src/settlement_lock_manager.rs` -Resource lock management functions: -- `lock_resources()`: Locks resources when proposal is created -- `unlock_resources()`: Releases locks -- `release_locked_resources()`: Alias for unlock (for clarity in rejection paths) - -### 3. `contracts/utility_contracts/src/settlement.rs` -Main settlement contract with: - -#### Constants -- `MIN_SETTLEMENT_WINDOW = 60` (1 minute) -- `MAX_SETTLEMENT_WINDOW = 604800` (7 days) - -#### Error Codes -- `DeadlineExceeded = 1`: Settlement deadline exceeded -- `InvalidSettlementWindow = 2`: Window outside valid range -- `ProposalNotFound = 3`: Proposal doesn't exist -- `AlreadyFinalized = 4`: Proposal already finalized -- `Unauthorized = 5`: Unauthorized access - -#### Functions - -**`propose_settlement()`** -- Validates settlement_window is in range [60, 604800] -- Requires payer authorization -- Calculates settlement_deadline = submission_timestamp + settlement_window -- Locks resources -- Stores proposal - -**`finalize_settlement()`** -- **CRITICAL**: First operation checks `contract.ledger().timestamp() > settlement_deadline` -- Hard deadline enforcement with 0 grace period -- Panic with `DeadlineExceeded` if deadline passed -- Releases resources before panicking (atomic rollback) -- Requires payee authorization -- Prevents double finalization - -**`get_proposal()`** -- Retrieves proposal by ID - -**`is_deadline_exceeded()`** -- Checks if a proposal's deadline has passed - -### 4. Updated `contracts/utility_contracts/src/lib.rs` -Added module declarations: -```rust -pub mod settlement; -pub mod settlement_lock_manager; -pub mod settlement_types; -``` - -## Implementation Details - -### Deadline Enforcement -The deadline check is implemented as the **first operation** in `finalize_settlement()`: - -```rust -let current_timestamp = env.ledger().timestamp(); -if current_timestamp > proposal.settlement_deadline { - release_locked_resources(&env, &mut proposal, &token_address); - panic_with_error!(&env, SettlementError::DeadlineExceeded); -} -``` - -This ensures: -1. **Zero grace period** - strictly rejects if current_timestamp > deadline -2. **No state mutation before check** - happens before any other logic -3. **Atomic rollback** - releases locks before panicking (Soroban's panic reverts all state changes) - -### Resource Locking -Resources are locked when a proposal is created and automatically released on: -- Successful finalization -- Deadline expiration (before panic) -- Any error condition - -Since Soroban's `panic_with_error!` reverts all state changes in the current transaction, the lock release call before panic ensures proper cleanup. - -### Settlement Window Validation -The `propose_settlement()` function validates the window parameter: -```rust -if settlement_window < MIN_SETTLEMENT_WINDOW || settlement_window > MAX_SETTLEMENT_WINDOW { - panic_with_error!(env, SettlementError::InvalidSettlementWindow); -} -``` - -This enforces the required bounds of 60 seconds (1 minute) to 604800 seconds (7 days). - -## Tests Included - -The implementation includes comprehensive tests: - -1. **`test_settlement_window_validation()`** - - Tests window < 60 seconds fails - - Tests window > 7 days fails - -2. **`test_settlement_finalized_before_deadline_succeeds()`** - - Settlement at timestamp 1200 with deadline 1300 succeeds - -3. **`test_settlement_finalized_exactly_at_deadline_succeeds()`** - - Settlement at exactly deadline timestamp succeeds - -4. **`test_settlement_finalized_after_deadline_fails()`** - - Settlement 1 second after deadline panics with DeadlineExceeded (error code 1) - -5. **`test_settlement_window_bounds()`** - - Tests minimum valid window (60 seconds) - - Tests maximum valid window (604800 seconds) - -6. **`test_is_deadline_exceeded()`** - - Tests deadline checking before and after expiry - -7. **`test_double_finalization_fails()`** - - Ensures proposals cannot be finalized twice - -## Security Features - -1. **Hard Deadline**: No grace period, strictly enforces timestamp check -2. **Authorization**: Requires payer auth for proposal, payee auth for finalization -3. **Atomic Operations**: State reverts on any error via panic mechanism -4. **Resource Safety**: Locks released before panic to prevent resource leaks -5. **Front-running Protection**: Deadline enforcement prevents stale settlement execution - -## Current Status - -### ✅ Implemented -- Settlement proposal struct with all required fields -- Deadline calculation (submission_timestamp + settlement_window) -- Hard deadline enforcement in finalize_settlement() -- Settlement window bounds validation [60, 604800] -- Resource locking/unlocking mechanism -- All required error types -- Comprehensive test suite -- Module integration into lib.rs - -### ⚠️ Note on Build Errors -The repository currently has 128 existing compilation errors in other parts of the codebase that are unrelated to the settlement feature. The settlement module itself is correctly implemented according to the specification. These existing errors need to be fixed separately before the entire project can compile. - -## Next Steps - -To complete this feature: -1. Fix the 128 existing compilation errors in the main codebase -2. Run the settlement tests: `cargo test --package utility_contracts settlement` -3. Perform integration testing with the token contract for actual resource locking -4. Security audit of the deadline enforcement logic -5. Deploy to testnet and verify behavior - -## Compliance with Requirements - -| Requirement | Status | Implementation | -|------------|--------|----------------| -| settlement_deadline field (u64) | ✅ | In SettlementProposal struct | -| Deadline check first operation | ✅ | First line in finalize_settlement() | -| Zero grace period | ✅ | Strict > comparison | -| Window range [60, 604800] | ✅ | Constants + validation | -| Atomic resource release | ✅ | release_locked_resources() before panic | -| ledger().timestamp() usage | ✅ | Used for deadline comparison | -| Max delay ≤ deadline - submission | ✅ | Enforced by timestamp check | -| Test cases (a)-(d) | ✅ | All implemented in mod test | - -All technical invariants and implementation blueprint requirements have been fulfilled. diff --git a/contracts/docs/security/macro-auth.md b/contracts/docs/security/macro-auth.md deleted file mode 100644 index 3dfc3c0..0000000 --- a/contracts/docs/security/macro-auth.md +++ /dev/null @@ -1,78 +0,0 @@ -# Mint/Burn Authorization — Model & Safety Invariants - -Issue #4 — "Custom Validation Macro Override in Resource Tokenization -Authorization" - -## Scope correction - -The issue describes a procedural macro `#[requires_role(Role::Minter)]` whose -expansion skips the authorization check when an `#[allow(unused)]` attribute is -present, allowing unauthorized minting. - -**No such macro exists in this codebase.** There is no `#[requires_role]` -attribute, no `Role` enum, and no `role_check` proc-macro crate. Authorization is -not attribute-driven, so the described macro-expansion bypass is not applicable. - -This document records the **actual** authorization model, the invariant it -upholds, and the tests that now lock it in. - -## Actual authorization model (`resource-token`) - -``` -mint(env, to, amount) -> authorize_mint(env) -> authorize_with_chain(env) -burn(env, from, amount) -> authorize_burn(env) -> authorize_with_chain(env) - -authorize_with_chain(env): - admin = get_admin(env) // panics "NoAdmin" if unset - admin.require_auth() // panics if the admin has not authorized -``` - -`authorize_*` is the **first statement** of `mint`/`burn`, before any state is -read or written. The gate is `Address::require_auth()`, which the Soroban host -validates against the actual authorization context — it cannot be spoofed by an -intermediate contract in the call chain. There is no code path that reaches the -balance/supply mutation without passing `admin.require_auth()`. - -### Invariant - -``` -∀ mint/burn operation: the admin has authorized the operation (require_auth) -``` - -## Why this could regress silently (the real gap the issue points at) - -Every pre-existing test uses `env.mock_all_auths()`, which auto-approves **all** -authorization. Under that mode the gate is never exercised: a regression that -deleted `authorize_mint()`/`authorize_burn()` from `mint`/`burn` would still pass -the entire suite. - -### Tests added - -`contracts/resource-token/src/test.rs`: - -- `test_mint_rejected_without_authorization` — after setup, drop all auth with - `env.set_auths(&[])`; `mint` must panic. -- `test_burn_rejected_without_authorization` — same, for `burn`. -- `test_mint_rejected_without_auth_leaves_state_unchanged` — `try_mint` returns - `Err` and neither balance nor total supply changes. - -These exercise the gate with an empty authorization set, so removing or bypassing -the authorization call now fails the suite. - -## Known limitation / recommended follow-up (out of scope here) - -`authorize_with_chain` only honors the **admin**. The contract also exposes -`authorize_operator` / `is_valid_operator` and documents that "the admin or a -valid operator can mint", but the mint/burn path **never checks operators** — an -operator cannot actually mint, because `require_auth()` is only ever called on -the admin's address. - -Making operator-delegated minting work securely requires threading the caller's -`Address` into `mint`/`burn` (so the contract can `require_auth()` the operator -and verify it is currently valid). That is a **breaking signature change** and is -intentionally left as a separate change; the misleading documentation should be -corrected at the same time. - -The current behaviour is **safe** (only the admin can mint/burn); the limitation -is reduced functionality, not an authorization bypass. -``` diff --git a/contracts/docs/specs/mint-atomicity.md b/contracts/docs/specs/mint-atomicity.md deleted file mode 100644 index 07212a3..0000000 --- a/contracts/docs/specs/mint-atomicity.md +++ /dev/null @@ -1,76 +0,0 @@ -# Mint / Burn Supply-Cap Atomicity (resource-token) - -Issue #1 — "Race Condition in Resource Tokenization Mint/Burn State Machine" - -## Summary - -`resource-token` mints/burns tokens that are backed **1:1** by real-world resource -deposits. The invariant is: - -``` -total_supply == Σ(balances) <= MAX_SUPPLY -``` - -`MAX_SUPPLY = 1_000_000_000_000_000` (10^15 base units). - -## What the original code was missing - -`mint()` overflow-checked `total_supply` but **never enforced an upper bound** — -supply could grow without limit, so the `<= MAX_SUPPLY` half of the invariant was -not enforced at all. `burn()` used unchecked subtraction (`-`), which silently -wraps in builds without `overflow-checks` (the workspace release profile does not -enable them, and per-crate `[profile.release]` is ignored for workspace members). - -## The fix - -- `mint()` computes `new_supply = current_supply.checked_add(amount)` and **rejects - the call** (`panic!("Max supply exceeded")`) when `new_supply > MAX_SUPPLY`, - **before** writing any state. -- `burn()` uses `checked_sub` for both balance and total supply. - -The check-then-write ordering means no partial state is committed on rejection. - -## On the "race condition" framing - -The issue describes two `mint()` calls in the **same ledger** both observing -`total_supply == MAX_SUPPLY - 1` and both proceeding. That cannot happen on -Stellar/Soroban: - -- Transactions are applied **serially** by the host. There is no concurrent - execution of two invocations against the same contract state. -- Each transaction reads the **committed** state left by the previous one and its - writes are atomic with respect to other transactions. - -So a cross-transaction "check-and-set race" within a ledger does not exist, and -the remedies proposed for that model do not apply here: - -- **`MINT_INFLIGHT` lock** — would only matter for *re-entrancy* (a nested call - back into `mint` within one invocation). `mint`/`burn` make no external - contract calls, so there is no re-entrancy vector to guard. Adding a lock would - be dead code. -- **Two-phase commit + background finalization** — Soroban has no background - processes and no cross-ledger uncommitted state; there is nothing to finalize - asynchronously. - -The real, enforceable defect was the missing cap. Enforcing it (plus -overflow-safe arithmetic) fully restores the invariant. - -## Tests - -`contracts/resource-token/src/test.rs`: - -- `test_mint_up_to_max_supply_succeeds` — minting exactly `MAX_SUPPLY` is allowed. -- `test_mint_exceeding_max_supply_panics` — one unit past the cap is rejected. -- `test_mint_overflowing_supply_in_two_steps_panics` — the issue's `MAX_SUPPLY-1` - scenario, modelled as the serial calls Soroban actually performs. -- `test_repeated_mints_never_exceed_max_supply` — 100 sequential mints (the - "100 concurrent calls" analog) keep `total_supply <= MAX_SUPPLY` and - `total_supply == Σ(balances)` at every step. -- `test_burn_after_max_supply_allows_reminting` — burning frees cap headroom. - -## Note on `MIN_MINT_AMOUNT` - -The issue also lists `MIN_MINT_AMOUNT = 1_000_000`. It is **not** enforced here: -it is a dust-control policy orthogonal to the supply invariant, and enforcing it -would break the contract's existing small-amount mint/burn behaviour and test -suite. It can be added as a separate, deliberate policy change if desired. diff --git a/contracts/docs/specs/reconciliation-scaling.md b/contracts/docs/specs/reconciliation-scaling.md deleted file mode 100644 index 9eb11b3..0000000 --- a/contracts/docs/specs/reconciliation-scaling.md +++ /dev/null @@ -1,80 +0,0 @@ -# Deposit → Token Reconciliation Scaling — Overflow Safety - -Issue #5 — "Integer Scaling Protection Failure in Resource Deposit/Burnback -Reconciliation" - -## Goal - -Convert an off-chain resource deposit attestation into the number of on-chain -tokens to mint: - -``` -tokens_to_mint = floor(deposit_amount × TOKEN_SCALE_FACTOR / ASSET_PRECISION) -``` - -- `TOKEN_SCALE_FACTOR = 10¹⁸` (Soroban 18-decimal token standard) -- `ASSET_PRECISION ∈ [1, 10¹²]` (commodity micro-unit precision, configurable) - -**Invariant:** `tokens_minted × ASSET_PRECISION ≈ deposit_amount × TOKEN_SCALE_FACTOR` -within < 1 base unit (floor rounding). - -## The defect - -A naive `u128` implementation computes `deposit_amount × 10¹⁸` first. For large -deposits (and the crafted `deposit = u128::MAX`, `ASSET_PRECISION = 1`) that -product exceeds `u128::MAX` and **wraps silently**, minting an amount wildly -divorced from the deposit — either a tiny fraction of, or vastly more than, the -backing resource. - -## The implementation - -`contracts/common/src/scaling.rs` (pure `#![no_std]`, no new dependencies): - -- `reconcile_tokens(deposit_amount, asset_precision) -> Result` - - validates `ASSET_PRECISION ∈ [1, 10¹²]` → `Err(InvalidPrecision)`; - - computes the scaling with the exact 256-bit `mul_div_floor` from - [`crate::weighted_rate`] — the `deposit × 10¹⁸` product is held in full - 256-bit precision and divided exactly, so it **never wraps**; - - returns `Err(Overflow)` if the mathematically-correct token amount exceeds - `u128::MAX` (instead of a silently wrapped value). -- `scale(amount, scale_factor, precision)` — the same, with a caller-supplied - scale factor. -- `is_valid_precision`, `is_safe_deposit`, and `MAX_SAFE_DEPOSIT` helpers for - callers that want the blueprint's conservative early-reject guard. - -### Rounding - -Floor is used deliberately: the contract must never mint **more** tokens than the -deposit backs. The error is strictly `< 1` base unit, satisfying the `|result − -exact| ≤ 1` requirement (in fact `< 1`). - -### Why not a 512-bit `uint`-based `SafeScale` - -The blueprint proposes a `(numerator, denominator)` struct over the `uint` crate -for 512-bit math. It is unnecessary: both operands are `u128`, so their product -is at most 256 bits, which `mul_div_floor` already handles exactly with no new -dependency and no allocation. The crate stays `no_std` and dependency-free. - -## Tests (`contracts/common/src/scaling.rs`) - -- simple conversions, zero deposit; -- precision bounds rejection (`0`, `10¹² + 1`, and the inclusive endpoints); -- the crafted `u128::MAX` overflow is **rejected, not wrapped**; -- `MAX_SAFE_DEPOSIT` boundary (no false overflow at the limit; overflow one past); -- large deposit with large precision still resolves; -- floor never over-mints, error < 1 base unit; -- 5000-iteration deterministic property sweep over - `deposit ∈ [0, MAX_SAFE_DEPOSIT]`, `precision ∈ [1, 10¹²]`, asserting **exact** - equality with a native `u128` reference. - -Run: `cargo test --package utility-contracts-common` - -## Wiring - -There is no `reconcile_deposit` contract in the repository today (the issue's -`contracts/src/resource_tokenization/...` paths do not exist). The verified -primitive lives in `common` so any reconciliation entry point — e.g. a future -`reconcile_deposit` in `resource-token`, or the supply accounting in -`utility_contracts` once that crate compiles — can call -`utility_contracts_common::scaling::reconcile_tokens` instead of unchecked -`u128` multiplication. diff --git a/contracts/docs/specs/tariff-precision.md b/contracts/docs/specs/tariff-precision.md deleted file mode 100644 index 486658f..0000000 --- a/contracts/docs/specs/tariff-precision.md +++ /dev/null @@ -1,92 +0,0 @@ -# Tariff Time-Weighted Average — Precision & Overflow Safety - -Issue #2 — "Temporal Tariff State Calculator Integer Precision Loss in -Time-Weighted Rate Averaging" - -## Goal - -Compute the time-weighted average rate over tariff windows - -``` -weighted_avg = Σ(rate_i × duration_i) / Σ(duration_i) -``` - -with **no intermediate overflow** and **no precision loss**, for the full input -domain (`rate` up to 18-decimal token units, `duration` up to a 30-day window, -up to `MAX_TARIFF_INTERVALS = 2880` intervals). - -Invariant restored: - -``` -∀ schedules: Σ(rate_i × duration_i) / Σ(duration_i) ∈ [min_rate, max_rate] -``` - -## The two defects - -1. **Overflow.** The tariff calculator multiplied with `saturating_mul`. On - overflow that *silently clamps* to `u128::MAX` and produces a wrong (capped) - average instead of failing — exactly the kind of silent corruption that - yields the reported double-digit billing error. -2. **Precision loss.** `Σ / total` via integer division truncates. The naive - "fix" of reordering to `rate_i / total × duration_i` is **worse**: it throws - away the fractional part of *every* term before summing. - -## The implementation - -`contracts/common/src/weighted_rate.rs` (pure `#![no_std]`, no dependencies): - -- `mul_full(a, b) -> (hi, lo)` — exact 128×128 → 256-bit multiply (two `u128` - limbs, schoolbook on 64-bit halves). -- `add_256` — 256-bit accumulation with overflow detection. -- `div_256_by_128(hi, lo, d)` — exact restoring long division, returns - `(quotient, remainder)`; `None` if the quotient would exceed `u128`. -- `round_half_up(q, r, d)` — `ROUND_HALF_UP` using the remainder (overflow-safe - comparison `2r ≥ d`). -- `mul_div_floor` / `mul_div_round` — `a*b/d` with no intermediate overflow. -- `weighted_average(&[(rate, duration)])` — accumulates the numerator in full - 256-bit precision, divides exactly, rounds half-up. -- `interval_product_fits_u128(rate, duration)` — optional per-interval - pre-validation for callers that want to reject extreme schedules at creation - time (blueprint step 3). - -### Why full-width instead of `Decimal128` - -A `(mantissa, scale)` decimal type with a 38-digit intermediate scale (as the -blueprint sketches) still rounds at each `mul`/`div`. Accumulating the numerator -in **exact 256-bit integers** and dividing once is simpler *and* exact — the -relative error is **0** across the domain, far tighter than the `1e-15` target. -No big-int crate is needed and the crate stays `no_std`. - -### Overflow guarantees - -- Per-term `rate × duration`: exact (256-bit), never overflows. -- Numerator sum over N intervals: exact unless it exceeds 2²⁵⁶ — unreachable for - any real schedule (`2880 × max_term ≪ 2²⁵⁶`); reported as `None` if it ever - occurs, never silently wrong. -- Result: exact `u128` (the weighted average is bounded by `max_rate`, so it - always fits); `None` only in degenerate/empty/zero-duration inputs. - -## Tests - -`contracts/common/src/weighted_rate.rs` `#[cfg(test)]`: - -- `mul_full` low-bits-match-`wrapping_mul` and known high products. -- `mul_div_round` half-up behaviour and zero-divisor / quotient-overflow `None`. -- weighted-average: constant rate, two/weighted windows, empty/zero-duration, - 18-decimal 30-day scale, beyond-`u128` numerator (exact via 256-bit). -- `weighted_average_property_small_domain` — 2000-iteration deterministic sweep - over `rate ∈ [1, 10¹⁸]`, `duration ∈ [60, 2_592_000]`, - `interval_count ∈ [1, 2880]`, asserting **exact** equality with a native - reference (relative error 0). - -## Wiring into the tariff oracle - -The production consumer is `TariffOracle::calculate_flow_for_period` -(`contracts/utility_contracts/src/tariff_oracle.rs`), which currently uses -`saturating_mul` + truncating division. That crate **does not compile today** -(129+ pre-existing Soroban-SDK-23 errors — see -`contracts/utility_contracts/COMPILATION_STATUS.md`), so this PR lands the -verified algorithm in the `common` crate. Once `utility_contracts` builds again, -`calculate_flow_for_period` should delegate to -`utility_contracts_common::weighted_rate::weighted_average` instead of the -saturating/truncating arithmetic. diff --git a/contracts/meter-aggregator/README.md b/contracts/meter-aggregator/README.md deleted file mode 100644 index ea2abe2..0000000 --- a/contracts/meter-aggregator/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# meter-aggregator - -Per-device meter reading aggregation with **bounded storage**. - -## Why - -Appending every raw meter reading to an unbounded per-device vector exhausts -Soroban contract storage. At one reading every ~5 seconds (~17,280/day) a naive -design overruns the contract storage budget within hours, after which all further -readings *and* settlements for that device fail — a cheap denial of service. - -This contract keeps live storage bounded regardless of device lifetime or -submission frequency. - -## How - -- **Raw readings** are stored under individual keys `RawReading(device, seq)` - with a monotonically increasing sequence number (O(1) append; seq order == time - order). -- On every submission the value is folded into the matching **hourly** and - **daily** rollup buckets using overflow-checked `i128` addition. -- Raw readings older than `MAX_RAW_RETENTION_SECS` (7 days) are pruned **inline**, - amortized to O(1) per submission via a watermark cursor `PruneCursor(device)`, - deleting at most `PRUNE_BATCH_SIZE` (10) entries per call so a backlog drains - over several submissions instead of blowing one call's instruction budget. -- Long-term volume lives in compact rollup buckets, read by - `get_aggregated_volume` which prefers **daily → hourly → raw** in that order. -- `rollup_day` consolidates a completed day by reclaiming its now-redundant - hourly buckets (the daily total is maintained incrementally), keeping - hourly-bucket growth bounded too. - -## Public API - -| fn | description | -|----|-------------| -| `initialize(admin)` | one-time admin setup | -| `submit_reading(device, source, value) -> seq` | store + rollup + inline prune | -| `prune(device) -> u32` | manual batch prune (callable by anyone) | -| `rollup_day(device, day_epoch) -> i128` | admin; reclaim a day's hourly buckets | -| `get_aggregated_volume(device, from_ts, to_ts) -> i128` | tiered windowed total | -| `get_hourly_bucket` / `get_daily_bucket` / `get_raw_reading` | views | -| `get_prune_cursor` / `get_reading_count` / `get_live_reading_count` | views | - -## Constants - -| name | value | meaning | -|------|-------|---------| -| `MAX_RAW_RETENTION_SECS` | `604_800` | 7-day raw retention window | -| `PRUNE_BATCH_SIZE` | `10` | max deletions per call | -| `ROLLUP_INTERVAL_SECS` | `3_600` | hourly bucket width | -| `SECONDS_PER_DAY` | `86_400` | daily bucket width | -| `FIXED_POINT_SCALE` | `10_000_000` | 7-decimal fixed point | - -## Limitation - -Sub-day query resolution is retained only while hourly buckets exist. After a day -is consolidated via `rollup_day` (and its raw readings pruned), that day is -queryable at day granularity only. Full-day and multi-day windows remain exact. - -## Test - -```sh -cargo test --package meter-aggregator -``` - -Covers: hourly/daily rollup correctness, daily fast-path reads, `rollup_day` -reclamation, overflow rejection, negative-value rejection, the pruning retention -boundary, batch-size limiting, and end-to-end storage-exhaustion prevention. diff --git a/contracts/resource-token/IMPLEMENTATION_SUMMARY.md b/contracts/resource-token/IMPLEMENTATION_SUMMARY.md deleted file mode 100644 index 0bcf339..0000000 --- a/contracts/resource-token/IMPLEMENTATION_SUMMARY.md +++ /dev/null @@ -1,232 +0,0 @@ -# Resource Token Contract - Implementation Summary - -## Overview - -This document summarizes the implementation of the resource token contract with full call-chain verification to prevent authorization spoofing attacks. - -## Problem Addressed - -The original issue was that authorization checks only inspected the immediate caller address, making the system vulnerable to proxy attacks where: -1. An admin deploys a malicious contract -2. The admin calls their malicious contract -3. The malicious contract invokes the resource token contract -4. The resource token would accept the call because it appears to come from an authorized context - -## Solution Implemented - -We implemented **full call-chain verification** using Soroban's authentication framework: - -### 1. Core Authorization (`auth.rs`) - -- **`authorize_mint()`** and **`authorize_burn()`**: Entry points for authorization -- **`authorize_with_chain()`**: Core function that uses `require_auth()` to validate admin authorization -- Uses Soroban's `require_auth()` which validates cryptographic signatures, not just addresses -- This prevents spoofing because the authorization must come from the actual admin's private key - -### 2. Operator Delegation (`operators.rs`) - -- **`authorize_operator(operator, expiration)`**: Admin can delegate mint/burn privileges with time limits -- **`revoke_operator(operator)`**: Admin can revoke delegations -- **`is_valid_operator(operator)`**: Check if an operator is authorized and not expired -- Max delegation period: 30 days (TTL_OPERATOR_DELEGATION = 2,592,000 seconds) -- Includes nonce-based replay protection - -### 3. Admin Management (`admin.rs`) - -- **`set_admin(new_admin)`**: Set or change the admin address -- **`get_admin()`**: Query the current admin -- Can only be called during initialization or by the current admin - -### 4. Storage (`storage.rs`) - -- Defines all storage keys: Admin, Operator(Address), Nonce(Address), Balance(Address), TotalSupply -- Helper functions for safe storage access -- TTL management for operator delegations - -### 5. Main Contract (`lib.rs`) - -- **`initialize(admin)`**: Set up the contract -- **`mint(to, amount)`**: Mint tokens (requires admin authorization) -- **`burn(from, amount)`**: Burn tokens (requires admin authorization) -- **`balance(address)`**: Query balance -- **`total_supply()`**: Query total supply -- Operator management functions - -## Security Guarantees - -### How Authorization Works - -1. **Cryptographic Validation**: Soroban's `require_auth()` validates signatures from private keys, not addresses -2. **No Address Spoofing**: Intermediate contracts cannot fake authorization -3. **Delegation Control**: Operators have time-limited permissions (max 30 days) -4. **Nonce Protection**: Each delegation operation increments a nonce to prevent replay attacks - -### Key Security Properties - -✅ **Admin-only mint/burn**: Only the admin can authorize minting and burning -✅ **Time-limited delegation**: Operator permissions automatically expire -✅ **Revocation support**: Admin can revoke operator permissions at any time -✅ **Nonce-based replay protection**: Prevents reuse of old authorization signatures -✅ **Balance overflow protection**: Safe arithmetic prevents overflows -✅ **Input validation**: Amount validation, zero checks, etc. - -## Test Coverage - -All 19 tests passing ✅ - -### Test Categories - -1. **Initialization Tests** (2 tests) - - Initialize contract - - Prevent double initialization - -2. **Admin Operations** (3 tests) - - Direct admin mint - - Direct admin burn - - Admin transfer - -3. **Operator Tests** (4 tests) - - Operator mint - - Operator burn - - Expired operator rejection - - Revoked operator rejection - -4. **Authorization Tests** (2 tests) - - Unauthorized mint (with note on test environment) - - Unauthorized burn (with note on test environment) - -5. **Balance Tests** (3 tests) - - Multiple mints - - Multiple burns - - Nonexistent account query - -6. **Validation Tests** (3 tests) - - Zero amount rejection - - Insufficient balance rejection - - Input validation - -7. **Multi-operator Tests** (2 tests) - - Multiple operators coexist - - Operator cannot authorize other operators - -## Technical Details - -### Storage Keys - -```rust -pub enum DataKey { - Admin, // The admin address - Operator(Address), // Operator expiration timestamp - Nonce(Address), // Replay protection nonce - TotalSupply, // Total token supply - Balance(Address), // Token balances -} -``` - -### Constants - -- **TTL_OPERATOR_DELEGATION**: 30 days (2,592,000 seconds) -- **MAX_CHAIN_DEPTH**: 5 (defined but not strictly enforced in current implementation) - -### Authorization Flow - -``` -User calls mint/burn - ↓ -Contract calls authorize_mint/authorize_burn - ↓ -authorize_with_chain() is called - ↓ -admin.require_auth() validates signature - ↓ -If valid: operation proceeds -If invalid: panic with "not authorized" -``` - -## Files Created - -1. **`contracts/resource-token/src/lib.rs`** - Main contract implementation -2. **`contracts/resource-token/src/auth.rs`** - Authorization logic -3. **`contracts/resource-token/src/admin.rs`** - Admin management -4. **`contracts/resource-token/src/operators.rs`** - Operator delegation -5. **`contracts/resource-token/src/storage.rs`** - Storage definitions -6. **`contracts/resource-token/src/test.rs`** - Comprehensive test suite -7. **`contracts/resource-token/Cargo.toml`** - Package configuration -8. **`contracts/resource-token/README.md`** - User documentation - -## Building and Testing - -### Run Tests - -```bash -cargo test --package resource-token -``` - -**Result**: ✅ test result: ok. 19 passed; 0 failed; 0 ignored - -### Build Contract - -```bash -cargo build --release --target wasm32-unknown-unknown --package resource-token -``` - -(Note: Requires `wasm32-unknown-unknown` target: `rustup target add wasm32-unknown-unknown`) - -## Implementation Compliance - -### Requirements from Problem Statement ✅ - -1. ✅ **Authorization check validates full call chain** -2. ✅ **Admin authorization via DataKey::Admin** -3. ✅ **Operator delegation via DataKey::Operator(caller) with expiration** -4. ✅ **Max TTL of 30 days for operator delegation** -5. ✅ **Nonce-based replay protection via DataKey::Nonce(caller)** -6. ✅ **Call chain depth awareness (MAX_CHAIN_DEPTH = 5)** -7. ✅ **~10,000 instructions per auth check (typical for require_auth)** - -### Implementation Blueprint Steps ✅ - -- ✅ **Step 1**: Created `operators.rs` with `authorize_operator()` and `revoke_operator()` -- ✅ **Step 2**: Created `authorize_with_chain()` in `auth.rs` with full validation -- ✅ **Step 3**: Replaced direct admin checks in `mint()` and `burn()` with `authorize_with_chain()` -- ✅ **Step 4**: Added nonce-based replay protection in `storage.rs` -- ✅ **Step 5**: Added comprehensive tests covering all scenarios -- ✅ **Step 6**: All tests pass successfully - -## Security Audit Notes - -### Strengths - -1. Uses Soroban's native `require_auth()` which validates cryptographic signatures -2. Operator permissions are time-limited -3. Admin can revoke permissions at any time -4. Nonce-based replay protection -5. Safe arithmetic prevents overflows -6. Comprehensive input validation - -### Limitations - -1. Operator delegation currently only checked for expiration, not actively used in mint/burn (admin-only in current implementation) -2. Call chain depth limit (MAX_CHAIN_DEPTH) defined but not strictly enforced -3. No enumeration of active operators (could add if needed) - -### Recommendations - -1. ✅ Implemented: Admin-only authorization for mint/burn -2. ✅ Implemented: Time-limited operator delegations -3. ✅ Implemented: Revocation mechanism -4. ✅ Implemented: Nonce-based replay protection -5. Future: Consider adding operator-based mint/burn if needed -6. Future: Add strict call chain depth enforcement if needed - -## Conclusion - -The implementation successfully addresses the authorization spoofing vulnerability by: - -1. Using cryptographic signature validation (`require_auth()`) instead of address checks -2. Implementing time-limited operator delegations with expiration -3. Providing nonce-based replay protection -4. Including comprehensive test coverage (19/19 tests passing) -5. Following Soroban best practices for authentication - -The contract is ready for deployment and further testing on a Soroban testnet. diff --git a/contracts/resource-token/README.md b/contracts/resource-token/README.md deleted file mode 100644 index e3c9742..0000000 --- a/contracts/resource-token/README.md +++ /dev/null @@ -1,192 +0,0 @@ -# Resource Token Contract - -A secure Soroban smart contract implementing a token with mint/burn operations that includes full call chain verification to prevent authorization spoofing attacks. - -## Security Model - -### Problem Statement - -Traditional authorization checks that only inspect the immediate caller address are vulnerable to proxy attacks. A malicious contract deployed by an admin could act as a proxy, allowing unauthorized minting/burning by appearing to be called by an authorized address. - -### Solution - -This contract implements **full call-chain verification** that validates each hop in the contract invocation chain: - -1. **Admin Authorization**: Direct calls from the admin address are allowed -2. **Operator Delegation**: The admin can delegate mint/burn privileges to operators with time-limited authorizations (max 30 days) -3. **Expiration Checking**: All operator delegations include expiration timestamps and are validated before each operation -4. **Nonce-based Replay Protection**: Each delegation signature includes a nonce to prevent replay attacks -5. **Call Chain Depth Limits**: Maximum chain depth of 5 to prevent resource exhaustion - -## Architecture - -### Core Modules - -- **lib.rs**: Main contract implementation with public interface -- **auth.rs**: Authorization logic with full call chain verification (authorize_mint, authorize_burn) -- **admin.rs**: Admin management (set_admin, get_admin) -- **operators.rs**: Operator delegation management (authorize_operator, revoke_operator) -- **storage.rs**: Storage key definitions and helper functions -- **test.rs**: Comprehensive test suite - -### Key Functions - -#### Admin Functions - -- `initialize(admin: Address)` - Set up the contract with an initial admin -- `set_admin(new_admin: Address)` - Change the admin (admin only) -- `get_admin() -> Option
` - Query the current admin - -#### Operator Management - -- `authorize_operator(operator: Address, expiration: u64)` - Grant mint/burn privileges (admin only) -- `revoke_operator(operator: Address)` - Revoke operator privileges (admin only) -- `is_valid_operator(operator: Address) -> bool` - Check if an operator is currently authorized - -#### Token Operations - -- `mint(to: Address, amount: i128)` - Mint tokens (admin or valid operator only) -- `burn(from: Address, amount: i128)` - Burn tokens (admin or valid operator only) -- `balance(address: Address) -> i128` - Query token balance -- `total_supply() -> i128` - Query total token supply - -## Security Guarantees - -### Authorization Invariants - -For any mint/burn operation, the following must be true: - -``` -∀ operation ∈ {mint, burn}: - (caller == admin) ∨ - (∃ operator: - (caller == operator) ∧ - (delegation[operator].expiration > now) ∧ - (delegation[operator].nonce == expected_nonce)) -``` - -### Technical Bounds - -- **TTL_OPERATOR_DELEGATION**: 30 days (2,592,000 seconds) -- **MAX_CHAIN_DEPTH**: 5 invocation hops -- **Instruction cost per auth check**: ~10,000 instructions - -### Call Chain Verification - -The contract uses Soroban's `require_auth()` mechanism to validate authorization: - -1. **Direct Admin Call**: `admin.require_auth()` verifies the admin's signature -2. **Operator Call**: `operator.require_auth()` verifies the operator's signature AND checks delegation validity -3. **Proxy Prevention**: Intermediate contracts cannot spoof authorization because `require_auth()` validates cryptographic signatures, not just addresses - -## Testing - -The test suite covers: - -1. ✅ Direct admin call succeeds -2. ✅ Delegated operator call succeeds -3. ✅ Expired delegation fails -4. ✅ Unauthorized caller fails -5. ✅ Revoked operator fails -6. ✅ Multiple operators can coexist -7. ✅ Insufficient balance checks -8. ✅ Zero amount validation -9. ✅ Admin transfer functionality -10. ✅ Balance overflow protection - -### Running Tests - -```bash -cargo test --package resource-token -``` - -### Test Coverage - -```bash -cargo tarpaulin --package resource-token -``` - -## Build - -Build the contract: - -```bash -cargo build --package resource-token --target wasm32-unknown-unknown --release -``` - -Optimize the WASM: - -```bash -soroban contract optimize \ - --wasm target/wasm32-unknown-unknown/release/resource_token.wasm -``` - -## Deployment - -1. Build the optimized WASM -2. Deploy to network: - ```bash - soroban contract deploy \ - --wasm target/wasm32-unknown-unknown/release/resource_token.wasm \ - --source \ - --network - ``` -3. Initialize the contract: - ```bash - soroban contract invoke \ - --id \ - --source \ - --network \ - -- initialize \ - --admin - ``` - -## Usage Example - -```rust -use soroban_sdk::{Address, Env}; - -// Initialize -let admin = Address::from_string("GADMIN..."); -contract.initialize(admin.clone()); - -// Authorize an operator for 7 days -let operator = Address::from_string("GOPER..."); -let expiration = env.ledger().timestamp() + (7 * 86400); -contract.authorize_operator(operator.clone(), expiration); - -// Mint tokens -let recipient = Address::from_string("GRECIP..."); -contract.mint(recipient.clone(), 1000); - -// Check balance -let balance = contract.balance(recipient); -assert_eq!(balance, 1000); -``` - -## Gas Estimates - -Based on instruction counts (~10,000 per auth check): - -- Admin mint: ~15,000 instructions -- Operator mint: ~25,000 instructions (includes delegation check) -- Admin burn: ~15,000 instructions -- Operator burn: ~25,000 instructions -- Balance query: ~1,000 instructions - -## Security Audit Checklist - -- [x] Admin authorization properly enforced -- [x] Operator delegation includes expiration -- [x] Nonce-based replay protection implemented -- [x] Call chain depth limited -- [x] Balance overflow checks -- [x] Zero amount validation -- [x] Insufficient balance checks -- [x] Operator cannot authorize other operators -- [x] Expired delegations rejected -- [x] Revoked operators cannot operate - -## License - -See repository license. diff --git a/contracts/utility_contracts/src/ERRORS.md b/contracts/utility_contracts/src/ERRORS.md deleted file mode 100644 index 0808f8e..0000000 --- a/contracts/utility_contracts/src/ERRORS.md +++ /dev/null @@ -1,17 +0,0 @@ -# Utility-Protocol Contract - Error Codes - -This document provides a mapping of on-chain error codes to user-friendly explanations and suggested actions. When a transaction fails, the frontend can use this guide to display a helpful message instead of a raw error. - -| Code | Enum Name | User-Facing Message | Suggested Action | -|------|-----------|---------------------|------------------| -| 1 | `MeterNotFound` | The specified meter ID does not exist. | Please double-check the meter ID you entered. If you just registered, please wait a few moments for the network to update. | -| 2 | `OracleNotSet` | The price oracle has not been configured by the admin. | This is a contract configuration issue. Please contact the service provider. | -| 5 | `InvalidTokenAmount` | The amount for the transaction is invalid (e.g., zero or negative). | Please enter a positive amount for your top-up or withdrawal. | -| 10 | `PublicKeyMismatch` | The public key in the usage data does not match the one registered for the meter. | This could indicate a device configuration issue or a potential security problem. Please contact your utility provider. | -| 11 | `TimestampTooOld` | The usage data is too old and was rejected to prevent replay attacks. | Ensure your metering device's clock is synchronized. The issue should resolve itself on the next reading. | -| 15 | `MeterNotPaired` | The meter device has not been securely paired with the contract. | Please complete the pairing process for your meter before submitting usage data. | -| 19 | `AccountAlreadyClosed` | This meter account has already been closed. | You cannot perform actions on a closed account. Please register a new meter if you wish to continue service. | -| 20 | `InsufficientBalance` | Your account does not have enough funds to perform this action. | Please top up your meter balance to continue service or complete the transaction. | -| 21 | `UnauthorizedContributor` | The address used for this top-up is not authorized for this meter. | Only the meter owner or an authorized contributor (e.g., a roommate) can top up this meter. | -| 50 | `UnfairPriceIncrease` | The provider attempted to increase the rate by more than the allowed 10% in a single update. | The transaction was blocked to protect you from a sudden price spike. No action is needed on your part. | -| 51 | `BillingGroupNotFound` | The specified billing group does not exist. | Please ensure you have created a billing group for the parent account before attempting group operations. | \ No newline at end of file diff --git a/contracts/utility_contracts/src/GASLESS_RELAY_DOCS.md b/contracts/utility_contracts/src/GASLESS_RELAY_DOCS.md deleted file mode 100644 index 134d68f..0000000 --- a/contracts/utility_contracts/src/GASLESS_RELAY_DOCS.md +++ /dev/null @@ -1,276 +0,0 @@ -# Gasless Transaction Relay - Issue #131 - -## Overview - -The Gasless Transaction Relay is an EIP-2771 compatible system that enables the Utility Protocol to sponsor gas costs for approved operations during user onboarding. This removes friction for new users who would otherwise need to acquire XLM to pay gas fees. - -## Architecture - -### Core Components - -1. **Gasless Relay (`gasless_relay.rs`)** - - Main contract implementing EIP-2771 meta-transaction forwarding - - Manages nonce counters for replay protection - - Tracks sponsorship pool balance - - Forwards authenticated meta-transactions - -2. **Signature Verification (`gasless_relay_sig_verify.rs`)** - - Ed25519 signature verification for meta-transactions - - Timestamp validation (6-hour window) - - Approved forwarder validation - - Signer address recovery - - Nonce-based replay attack prevention - -3. **Sponsorship Policy Engine (`gasless_relay_policy.rs`)** - - Fine-grained control over which operations are sponsored - - Multiple sponsorship statuses (FullySponsored, PartiallySponsored, Suspended) - - Per-operation gas limits - - Daily transaction rate limiting - - Operation statistics tracking - - Policy suspension/resumption for operational control - -## Key Features - -### 1. EIP-2771 Compatibility - -The relay implements the EIP-2771 standard for meta-transactions: - -``` -User → signs meta-transaction → Forwarder → Relay contract → Target contract -``` - -Meta-transactions include: -- `from` - Originating user address -- `to` - Target contract address -- `value` - XLM amount in stroops -- `data` - Encoded function call -- `nonce` - For replay protection -- `gas_price` - Gas price offered -- `gas_limit` - Maximum gas -- `deadline` - Transaction deadline - -### 2. Replay Attack Prevention - -- **Nonce Management**: Each user has a sequential nonce that must increment -- **Forwarder Nonce Tracking**: Forwarders have independent nonce sequences -- **Signature Aging**: Signatures are only valid for 6 hours - -### 3. Rate Limiting - -- **Per-User Limits**: Configurable maximum transactions per time period -- **Per-Operation Limits**: Daily limits on specific operations -- **Dynamic Period Tracking**: Automatic reset at period boundaries - -### 4. Sponsorship Pool Management - -- **Balance Tracking**: Real-time pool balance monitoring -- **Deduction on Use**: Immediate deduction from pool when transaction is sponsored -- **Top-Up Functionality**: Admin can add funds to pool -- **Low Balance Warnings**: Can trigger alerts when balance is low - -### 5. Flexible Sponsorship Policies - -```rust -DetailedSponsorshipPolicy { - operation_id: Symbol, // Operation identifier - status: SponsorshipStatus, // Full, Partial, Suspended, NotSponsored - max_gas: u64, // Maximum gas per transaction - sponsorship_percentage: u32, // 0-100% sponsorship - daily_tx_limit: u32, // Transactions per day - cost_per_tx: i128, // Cost in stroops - created_at: u64, // Timestamp - updated_at: u64, // Last update - admin: Address, // Policy administrator - description: String, // Human-readable description -} -``` - -## Usage Flow - -### 1. Initialize the Relay - -```rust -relay.initialize(env, admin_address, initial_pool_balance)?; -``` - -### 2. Register Trusted Forwarder - -```rust -relay.register_forwarder(env, forwarder_address, public_key)?; -``` - -### 3. Create Sponsorship Policies - -```rust -relay.register_sponsorship_policy(env, operation_id, policy)?; -``` - -### 4. Set Rate Limits - -```rust -relay.set_rate_limit(env, user_address, max_txs_per_period, period_seconds)?; -``` - -### 5. Forward Meta-Transaction - -```rust -relay.forward_meta_transaction(env, meta_tx_request, signature)?; -``` - -## Error Handling - -The relay defines comprehensive error codes: - -| Error | Code | Meaning | -|-------|------|---------| -| InvalidSignature | 1 | Signature verification failed | -| NonceMismatch | 2 | Nonce is invalid or replay attempted | -| RateLimitExceeded | 3 | User exceeded rate limit | -| OperationNotSponsored | 4 | Operation not in approved list | -| UntrustedForwarder | 5 | Forwarder not in whitelist | -| DeadlineExpired | 6 | Meta-transaction deadline passed | -| InsufficientSponsorshipBalance | 7 | Pool doesn't have enough funds | -| PolicyRejected | 8 | Operation failed policy check | - -## Security Considerations - -### 1. Signature Verification - -- Ed25519 signatures verified before processing -- Timestamps checked to prevent replay via old signatures -- Public key validation against approved forwarders - -### 2. Nonce Management - -- Sequential nonce enforcement prevents replay attacks -- Nonce increments required for each new transaction -- Independent nonce sequences for different forwarders - -### 3. Rate Limiting - -- Prevents denial-of-service through sponsorship abuse -- Per-user and per-operation limits -- Automatic reset at period boundaries - -### 4. Access Control - -- Admin-only operations for configuration -- Policy management restricted to admins -- Forwarder registration requires admin approval - -### 5. Pool Protection - -- Balance checks before each transaction -- Immediate deduction to prevent double-spending -- Emergency suspension capability for policies - -## Operational Procedures - -### Monitoring Pool Balance - -```rust -let balance = relay.get_sponsorship_pool_balance(env); -if balance < CRITICAL_THRESHOLD { - // Alert admin -} -``` - -### Checking User Nonce - -```rust -let current_nonce = relay.get_nonce(env, user_address); -``` - -### Suspending an Operation - -```rust -engine.suspend_policy(env, operation_id)?; -``` - -### Resuming an Operation - -```rust -engine.resume_policy(env, operation_id, SponsorshipStatus::FullySponsored)?; -``` - -### Topping Up Sponsorship Pool - -```rust -relay.top_up_sponsorship_pool(env, amount)?; -``` - -## Performance Characteristics - -- **Nonce Lookup**: O(1) per user -- **Policy Lookup**: O(n) where n = number of policies (typically small) -- **Rate Limit Check**: O(1) per user -- **Signature Verification**: Native Soroban crypto (optimized) - -## Testing - -The implementation includes three levels of tests: - -1. **Unit Tests** (`gasless_relay_tests.rs`) - - 15 focused test cases - - Tests individual component functionality - - Validates error conditions - -2. **Integration Tests** (`gasless_relay_integration_tests.rs`) - - 16 comprehensive scenarios - - Tests cross-component interactions - - Validates complete workflows - - Covers edge cases and recovery scenarios - -3. **Property Tests** - - Can be added using quickcheck for fuzzing - - Test invariants across random inputs - -## Future Enhancements - -1. **Batch Meta-Transactions**: Support multiple operations per relay call -2. **Conditional Sponsorship**: Sponsor only if certain conditions are met -3. **Dynamic Pricing**: Adjust sponsorship costs based on pool balance -4. **Multi-Sig Approval**: Require multiple admins to top up pool -5. **Statistics Export**: Detailed reporting on sponsorship usage -6. **Cross-Contract Integration**: Relay calls to other contracts -7. **Emergency Mode**: Pause all sponsorship without suspending policies - -## Governance - -- **Admin Role**: Controls initialization, policy creation, forwarder registration -- **Policy Admin**: Can suspend/resume individual policies -- **Monitor Role**: Can check pool balance and statistics (future) - -## Cost Analysis - -Each sponsored transaction costs: -- Storage: ~100 bytes per rate limit entry -- Computation: ~5,000 stroops for relay overhead -- Gas: Depends on underlying operation - -Total cost to protocol: `sponsorship_cost + relay_overhead + gas_cost` - -## Migration Guide - -### Deploying to Production - -1. Deploy relay contract -2. Register trusted forwarders -3. Create initial sponsorship policies -4. Set conservative rate limits -5. Fund sponsorship pool -6. Monitor usage patterns -7. Adjust policies based on metrics - -### Rollback Plan - -- Suspend all policies: `suspend_policy()` for each -- Redirect traffic: Update forwarder configuration -- Drain pool: `get_sponsorship_pool_balance()` and manual transfer -- Disable relay: Empty pool, remove all policies - -## References - -- **EIP-2771**: https://eips.ethereum.org/EIPS/eip-2771 -- **Soroban Documentation**: https://developers.stellar.org/soroban -- **Project Issue #131**: Gasless Transaction Relay for User Onboarding diff --git a/docs/STREAM_INSURANCE_POOL_GOVERNANCE.md b/docs/STREAM_INSURANCE_POOL_GOVERNANCE.md deleted file mode 100644 index cda64eb..0000000 --- a/docs/STREAM_INSURANCE_POOL_GOVERNANCE.md +++ /dev/null @@ -1,301 +0,0 @@ -# Stream Insurance Pool Governance System - -## Overview - -The Stream Insurance Pool Governance system implements a decentralized "Community Insurance" mechanism that provides mutual aid for utility security. Users can opt into a shared insurance pool by paying premiums, and the pool automatically lends funds to members whose utility streams are about to fail due to missed deposits. - -## Key Features - -### 1. Community Mutual Aid -- **Pooled Safety Buffer**: Multiple users contribute to a shared insurance fund -- **Auto-Lending**: Automatic emergency funding when member streams are at risk -- **Risk Sharing**: Distributes individual risk across the community -- **Decentralized Governance**: Pool participants vote on key parameters - -### 2. Risk-Based Premium Calculation -- **Dynamic Pricing**: Premiums calculated based on individual risk assessment -- **Multi-Factor Risk Scoring**: Considers payment history, usage patterns, device security, and tenure -- **Fair Pricing**: Lower-risk users pay lower premiums, higher-risk users pay more -- **Transparent Scoring**: Risk factors are clearly defined and auditable - -### 3. Governance Mechanisms -- **Proposal System**: Members can propose changes to pool parameters -- **Voting Power**: Based on premium contributions and tenure in the pool -- **Quorum Requirements**: 20% of voting power must participate for valid decisions -- **Approval Threshold**: 51% approval required for proposal execution -- **Timelock**: 7-day voting period ensures deliberate decision-making - -## Architecture - -### Core Components - -#### InsurancePool -```rust -pub struct InsurancePool { - pub total_funds: i128, // Total pool balance - pub total_members: u32, // Number of active members - pub total_voting_power: i128, // Sum of all member voting power - pub created_at: u64, // Pool creation timestamp - pub governance_admin: Address, // Initial admin (can be changed via governance) - pub base_premium_rate_bps: i128, // Base premium rate (basis points) - pub risk_multiplier_max: i128, // Maximum risk multiplier - pub is_active: bool, // Pool operational status - pub emergency_pause: bool, // Emergency pause flag -} -``` - -#### InsurancePoolMember -```rust -pub struct InsurancePoolMember { - pub user: Address, // Member's address - pub premium_paid: i128, // Total premium contributed - pub join_timestamp: u64, // When member joined - pub last_claim_timestamp: u64, // Last claim submission time - pub claim_count: u32, // Number of claims made - pub risk_score: u32, // Current risk score (0-1000) - pub voting_power: i128, // Calculated voting power - pub is_active: bool, // Member status -} -``` - -#### GovernanceProposal -```rust -pub struct GovernanceProposal { - pub proposal_id: u64, // Unique proposal identifier - pub proposer: Address, // Who created the proposal - pub proposal_type: ProposalType, // Type of change proposed - pub description: Symbol, // Brief description - pub new_value: i128, // Proposed new value - pub created_at: u64, // Creation timestamp - pub voting_deadline: u64, // When voting ends - pub votes_for: i128, // Voting power supporting - pub votes_against: i128, // Voting power opposing - pub total_votes: i128, // Total voting power participated - pub is_executed: bool, // Whether proposal was executed - pub is_cancelled: bool, // Whether proposal was cancelled -} -``` - -### Risk Assessment System - -The system evaluates member risk across four dimensions: - -1. **Payment History Score (0-250 points)** - - PrePaid: Balance maintenance patterns - - PostPaid: Debt-to-collateral ratios - - Consistent positive balances = higher score - -2. **Usage Stability Score (0-250 points)** - - Peak usage vs. average usage ratios - - Stable consumption patterns = higher score - - High volatility = lower score - -3. **Device Security Score (0-250 points)** - - Device pairing status - - Heartbeat frequency and recency - - Proper cryptographic setup = higher score - -4. **Tenure Score (0-250 points)** - - Length of membership in pool - - Account age and history - - Longer tenure = higher score - -**Total Risk Score**: Sum of all dimensions (0-1000) -- Lower scores indicate lower risk -- Used to calculate premium multipliers (0.5x - 3.0x) - -### Premium Calculation - -``` -Base Premium = Monthly Usage Value × Base Premium Rate (BPS) -Risk Multiplier = 0.5 + (Risk Score / 1000) × 2.5 -Final Premium = Base Premium × Risk Multiplier -``` - -Constraints: -- Minimum Premium: 100 XLM -- Maximum Premium: 10,000 XLM -- Base Rate Range: 0.1% - 10% of monthly usage - -### Claim Processing - -#### Automatic Approval -Small claims are automatically approved and processed if: -- Claim amount ≤ 1% of total pool funds -- Member risk score ≤ 300 (low risk) -- Member is in good standing - -#### Manual Review Process -Larger claims require governance approval: -1. Member submits claim with reason -2. Community reviews claim details -3. Voting period for approval/rejection -4. If approved, funds are transferred - -#### Claim Limits -- Maximum claim: 10% of total pool funds -- Cooldown period: 30 days between claims -- Emergency override: Governance can approve exceptions - -### Governance Proposal Types - -1. **ChangePremiumRate**: Adjust base premium percentage -2. **ChangeRiskMultiplier**: Modify maximum risk multiplier -3. **ChangeMaxClaimAmount**: Adjust maximum claim limits -4. **AddMember**: Approve new member applications -5. **RemoveMember**: Remove problematic members -6. **EmergencyPause**: Pause pool operations -7. **ChangeGovernanceAdmin**: Transfer admin rights - -### Integration with Utility Contracts - -#### Fee Allocation -- 0.5% of every utility claim is allocated to the insurance pool -- Provides sustainable funding for the pool -- Creates alignment between utility usage and insurance funding - -#### Emergency Funding -When a member's utility stream is at risk: -1. System detects low balance or payment failure -2. If member is in insurance pool, automatic claim is triggered -3. Funds are transferred to member's meter balance -4. Member's claim history is updated - -#### Throttling Integration -- Insurance pool members get priority during network throttling -- Pool membership considered in priority calculations -- Provides additional utility security benefit - -## Usage Examples - -### Creating an Insurance Pool - -```rust -// Admin creates the pool with 1% base premium rate -UtilityContract::create_insurance_pool( - env, - admin_address, - 100, // 1% in basis points -)?; -``` - -### Joining the Pool - -```rust -// Calculate required premium for user's meter -let premium = UtilityContract::calculate_premium_amount( - env, - user_address, - meter_id, -)?; - -// Join the pool -UtilityContract::join_insurance_pool( - env, - user_address, - meter_id, - premium, -)?; -``` - -### Submitting a Claim - -```rust -// Submit emergency funding claim -let claim_id = UtilityContract::submit_insurance_claim( - env, - claimant_address, - meter_id, - requested_amount, - symbol_short!("EmergFund"), -)?; -``` - -### Creating Governance Proposals - -```rust -// Propose to change premium rate to 1.5% -let proposal_id = UtilityContract::create_governance_proposal( - env, - proposer_address, - ProposalType::ChangePremiumRate, - symbol_short!("NewRate"), - 150, // 1.5% in basis points -)?; -``` - -### Voting on Proposals - -```rust -// Vote in favor of the proposal -UtilityContract::vote_on_proposal( - env, - voter_address, - proposal_id, - true, // vote for -)?; -``` - -## Security Considerations - -### Access Control -- Only pool members can vote on proposals -- Minimum voting power required to create proposals (5% of total) -- Cooldown periods prevent spam claims -- Emergency pause mechanism for crisis situations - -### Economic Security -- Risk-based pricing prevents adverse selection -- Claim limits prevent pool drainage -- Diversified risk across multiple members -- Sustainable funding through utility fee allocation - -### Governance Security -- Quorum requirements prevent minority control -- Voting power based on stake and tenure -- Timelock periods allow for deliberation -- Transparent proposal and voting process - -## Benefits - -### For Individual Users -- **Utility Security**: Protection against service interruption -- **Lower Individual Risk**: Shared risk across community -- **Governance Participation**: Voice in pool management -- **Priority Access**: Benefits during network congestion - -### For the Ecosystem -- **Network Stability**: Reduced service interruptions -- **Community Building**: Shared incentives and cooperation -- **Sustainable Funding**: Self-funding through utility fees -- **Decentralized Governance**: Community-controlled parameters - -### For Utility Providers -- **Reduced Defaults**: Insurance covers payment gaps -- **Stable Revenue**: More predictable payment flows -- **Customer Retention**: Enhanced service reliability -- **Risk Mitigation**: Shared responsibility for customer defaults - -## Future Enhancements - -### Advanced Risk Models -- Machine learning-based risk assessment -- Integration with external credit scoring -- Dynamic risk adjustment based on market conditions -- Predictive analytics for claim probability - -### Cross-Pool Insurance -- Multiple specialized pools (residential, commercial, industrial) -- Inter-pool reinsurance mechanisms -- Risk transfer between pools -- Specialized coverage types - -### Integration Expansions -- Integration with DeFi lending protocols -- Automated market makers for premium pricing -- Tokenized insurance positions -- Cross-chain insurance coverage - -## Conclusion - -The Stream Insurance Pool Governance system creates a robust, community-driven mutual aid mechanism that enhances utility security while maintaining decentralized governance. By combining risk-based pricing, democratic decision-making, and automatic emergency funding, it provides a sustainable solution for utility payment security that benefits all participants in the ecosystem. \ No newline at end of file diff --git a/docs/UTILITY_ERRORS.md b/docs/UTILITY_ERRORS.md deleted file mode 100644 index cd8be5f..0000000 --- a/docs/UTILITY_ERRORS.md +++ /dev/null @@ -1,38 +0,0 @@ -# 🌍 Utility-Protocol Multi-Language Error Mapping - -This document provides a mapping of on-chain error codes to human-readable descriptions in multiple languages. This ensures accessibility for users in rural areas and non-English speaking regions (Issue #122). - -## Error Code Reference - -| Code | ID | Description | Yoruba | Hausa | Igbo | Spanish | French | -|------|----|-------------|--------|-------|------|---------|--------| -| 1 | `MeterNotFound` | Meter not registered. | A kò rí mita yìí. | Ba a sami mita ba. | Ahụghị mita a. | Medidor no encontrado. | Compteur non trouvé. | -| 5 | `InvalidTokenAmount` | Invalid token amount. | Iye owó kò tọ́. | Adadin kuɗi ba daidai ba. | Ego ezughị oke. | Cantidad de tokens inválida. | Montant de jetons invalide. | -| 11 | `TimestampTooOld` | Transaction expired. | Àkókò ti kọjá. | Lokaci ya ƙare. | Oge agwụla. | Transacción expirada. | Transaction expirée. | -| 15 | `MeterNotPaired` | Device not paired. | Ẹ̀rọ kò tíì so pọ̀. | Ba a haɗa na'ura ba. | Ejikọtaghị mita. | Dispositivo no vinculado. | Appareil non appairé. | -| 16 | `MeterPaused` | Meter is paused. | Mita ti dádúró. | An dakatar da mita. | Akwụsịrị mita a. | Medidor pausado. | Compteur en pause. | -| 19 | `AccountAlreadyClosed` | Account is closed. | Àkàǹtì ti tì. | An rufe asusu. | Emechiela akaụntụ a. | Cuenta ya cerrada. | Compte déjà fermé. | -| 20 | `InsufficientBalance` | Low balance. | Owó kò tó. | Kuɗi ba su isa ba. | Ego ezughị. | Saldo insuficiente. | Solde insuffisant. | -| 22 | `InDispute` | Service in dispute. | Àríyànjiyàn wà. | Akwai jayayya. | E nwere esemokwu. | Servicio en disputa. | Service en litige. | -| 44 | `ProviderNotVerified` | Provider not verified. | Olùpèsè kò fẹsẹ̀ múlẹ̀. | Ba a tabbatar da mai samarwa ba. | Akwadoghị onye na-enye ọrụ. | Proveedor no verificado. | Fournisseur non vérifié. | -| 49 | `InsufficientXlmReserve` | Gas reserve low. | Owó gas kò tó. | Gas ya yi ƙasa. | Ego gas dị ala. | Reserva de gas insuficiente. | Réserve de gas insuffisante. | - -## Backend Integration - -The backend service should intercept contract reverts, extract the `u32` error code, and look up the corresponding translation based on the user's localized settings. - -### Example Mapping (JSON) -```json -{ - "20": { - "en": "Insufficient balance to continue service.", - "yo": "Owó kò tó láti tẹ̀síwájú.", - "ha": "Kuɗi ba su isa su ci gaba da sabis ba.", - "ig": "Ego ezughị iji gaa n'ihu.", - "es": "Saldo insuficiente para continuar el servicio.", - "fr": "Solde insuffisant pour continuer le service." - } -} -``` - -**Last Updated**: March 26, 2026 \ No newline at end of file From 1862c1f428f840904c17486417f784dd1ff96952 Mon Sep 17 00:00:00 2001 From: Dabira Olaoluwa Date: Thu, 20 Aug 2026 23:43:33 -0700 Subject: [PATCH 2/2] docs: add minimal README pointing to CONTRACTS.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5db362 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Utility-Contracts + +This repository has consolidated its documentation. All information regarding the protocol setup, architecture, and core smart contract specifications has been moved to [`CONTRACTS.md`](CONTRACTS.md). + +Please refer to `CONTRACTS.md` as the canonical source of truth for the smart contracts.