This document describes the technical architecture of the PropChain smart contract system, including design patterns, data structures, and integration points.
PropChain is built on the Substrate blockchain framework using the ink! smart contract language. The system consists of multiple interconnected contracts that provide a comprehensive real estate tokenization platform.
- Purpose: Central registry for all tokenized properties
- Storage: Property metadata, ownership records, transfer history
- Key Features: Property registration, ownership verification, metadata management
- Purpose: Secure transfer of property ownership
- Storage: Escrow agreements, fund locks, release conditions
- Key Features: Multi-signature releases, time-locked transactions, dispute resolution
- Purpose: ERC-721 compatible NFT representation of properties
- Storage: Token ownership, transfer records, approval mechanisms
- Key Features: Fractional ownership, transfer restrictions, royalty enforcement
- Purpose: External data integration for property valuations
- Storage: Price feeds, valuation sources, confidence scores
- Key Features: Multiple oracle sources, price aggregation, outlier detection
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend UI │───▶│ Gateway API │───▶│ Smart Contract │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Off-chain Storage │ │ Blockchain │
│ (IPFS/Arweave) │ │ (Substrate) │
└─────────────────┘ └─────────────────┘
#[ink(storage)]
pub struct PropertyRegistry {
/// Mapping from property ID to property information
properties: Mapping<PropertyId, PropertyInfo>,
/// Mapping from owner to their properties
owner_properties: Mapping<AccountId, Vec<PropertyId>>,
/// Registry configuration
config: RegistryConfig,
/// Access control mappings
admins: Mapping<AccountId, bool>,
agents: Mapping<AccountId, bool>,
}- Singleton Pattern: Single registry instance
- Factory Pattern: Property creation through standardized methods
- Observer Pattern: Event emission for state changes
- Access Control: Role-based permissions
#[ink(storage)]
pub struct EscrowContract {
/// Active escrow agreements
escrows: Mapping<EscrowId, EscrowInfo>,
/// User's active escrows
user_escrows: Mapping<AccountId, Vec<EscrowId>>,
/// Property escrow history
property_escrows: Mapping<PropertyId, Vec<EscrowId>>,
/// Contract configuration
config: EscrowConfig,
}- Time Locks: Prevent premature fund release
- Multi-signature: Require multiple approvals
- Dispute Resolution: Mechanism for handling conflicts
- Reentrancy Protection: Prevent recursive calls
#[derive(Debug, Clone, PartialEq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct PropertyMetadata {
/// Physical address
pub location: Location,
/// Property specifications
pub specifications: PropertySpecs,
/// Legal information
pub legal_info: LegalInfo,
/// Valuation details
pub valuation: ValuationInfo,
/// Document references
pub documents: Vec<DocumentReference>,
}
#[derive(Debug, Clone, PartialEq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct Location {
pub address: String,
pub coordinates: Option<(f64, f64)>,
pub country: String,
pub region: String,
pub postal_code: String,
}#[derive(Debug, Clone, PartialEq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct OwnershipInfo {
pub owner: AccountId,
pub ownership_type: OwnershipType,
pub shares: u64, // For fractional ownership
pub acquired_at: Timestamp,
pub purchase_price: Balance,
}
pub enum OwnershipType {
Full,
Fractional(u64), // Number of shares
Leasehold(u64), // Lease duration
}- Purpose: Decentralized document storage
- Protocol: HTTP API calls to IPFS nodes
- Content: Property documents, images, legal papers
- Purpose: Real-time property valuations
- Protocol: Chainlink-compatible oracle feeds
- Data: Market prices, rental yields, appreciation rates
- Purpose: Identity verification and compliance
- Protocol: REST API integration
- Data: User verification status, risk assessments
interface PropertyRegistryProps {
contract: ContractPromise;
account: AccountId;
}
const PropertyRegistry: React.FC<PropertyRegistryProps> = ({
contract,
account
}) => {
// Component implementation
};interface AppState {
properties: PropertyInfo[];
userProperties: PropertyId[];
loading: boolean;
error: string | null;
}pub enum Role {
Admin,
Agent,
Owner,
Public,
}
#[ink(storage)]
pub struct AccessControl {
roles: Mapping<AccountId, Role>,
permissions: Mapping<(Role, Operation), bool>,
}| Role | Register | Transfer | Escrow | Admin |
|---|---|---|---|---|
| Admin | ✓ | ✓ | ✓ | ✓ |
| Agent | ✓ | ✓ | ✓ | ✗ |
| Owner | ✗ | ✓ | ✓ | ✗ |
| Public | ✗ | ✗ | ✗ | ✗ |
- Type checking for all parameters
- Range validation for numeric inputs
- Format validation for strings
- Sanitization of user inputs
#[ink(storage)]
pub struct ReentrancyGuard {
locked: bool,
}
impl ReentrancyGuard {
fn begin_reentrancy_check(&mut self) -> Result<(), Error> {
if self.locked {
return Err(Error::ReentrantCall);
}
self.locked = true;
Ok(())
}
fn end_reentrancy_check(&mut self) {
self.locked = false;
}
}- Efficient data structures
- Minimal storage operations
- Batch processing for bulk operations
- Lazy loading of expensive computations
// Use Mapping instead of Vec for large datasets
properties: Mapping<PropertyId, PropertyInfo> // O(1) access
// Use packed structs to reduce storage costs
#[derive(scale::Encode, scale::Decode)]
pub struct CompactPropertyInfo {
pub owner: AccountId, // 32 bytes
pub value: Compact<Balance>, // Variable bytes
pub flags: u8, // 1 byte
}- On-chain caching for frequently accessed data
- Off-chain indexing for complex queries
- Event-based cache invalidation
#[ink(message)]
pub fn batch_register_properties(
&mut self,
properties: Vec<PropertyMetadata>
) -> Result<Vec<PropertyId>, Error> {
let mut results = Vec::new();
for metadata in properties {
let id = self.register_property_internal(metadata)?;
results.push(id);
}
Ok(results)
}#[ink(message)]
pub fn get_property_summary(&self, id: PropertyId) -> PropertySummary {
let property = self.properties.get(&id).unwrap();
PropertySummary {
id,
owner: property.owner,
value: property.metadata.valuation.amount,
// Only compute expensive fields when needed
location: property.metadata.location.address.clone(),
}
}#[ink(storage)]
pub struct ProxyContract {
implementation: AccountId,
admin: AccountId,
}
impl ProxyContract {
#[ink(message)]
pub fn upgrade(&mut self, new_implementation: AccountId) -> Result<(), Error> {
if self.env().caller() != self.admin {
return Err(Error::Unauthorized);
}
self.implementation = new_implementation;
self.env().emit_event(Upgraded {
old: self.implementation,
new: new_implementation,
});
Ok(())
}
}- Snapshot: Create backup of current state
- Transform: Convert data to new format
- Validate: Verify data integrity
- Deploy: Deploy new contract
- Migrate: Transfer data to new contract
- Verify: Final validation and cleanup
- Versioned API endpoints
- Graceful degradation for old clients
- Migration windows and notifications
#[ink(event)]
pub struct PerformanceMetrics {
gas_used: Balance,
execution_time: u64,
operation: Operation,
timestamp: Timestamp,
}#[ink(message)]
pub fn health_check(&self) -> HealthStatus {
HealthStatus {
total_properties: self.property_count,
active_escrows: self.escrow_count,
last_operation: self.last_timestamp,
gas_balance: self.env().balance(),
}
}- Gas usage patterns
- Transaction success rates
- Error frequency analysis
- Performance benchmarking
- Threshold-based alerts
- Anomaly detection
- Performance degradation warnings
- Security incident notifications
- State channels for frequent transfers
- Rollups for batch operations
- Sidechains for specialized functionality
- Bridge contracts for asset transfer
- Standardized interfaces across chains
- Unified governance framework
- Automated property valuation
- Predictive analytics
- Risk assessment models
- Property-backed lending
- Yield farming opportunities
- Liquidity pools for real estate tokens