diff --git a/contracts/bridge/src/lib.rs b/contracts/bridge/src/lib.rs index 25a0e8d88..92fc28d10 100644 --- a/contracts/bridge/src/lib.rs +++ b/contracts/bridge/src/lib.rs @@ -15,20 +15,91 @@ mod bridge { #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum Error { + /// Caller is not authorized Unauthorized, + /// Token does not exist TokenNotFound, + /// Invalid chain ID InvalidChain, + /// Bridge not supported for this token BridgeNotSupported, + /// Insufficient signatures collected InsufficientSignatures, + /// Bridge request has expired RequestExpired, + /// Already signed this request AlreadySigned, + /// Invalid bridge request InvalidRequest, + /// Bridge operations are paused BridgePaused, + /// Invalid metadata InvalidMetadata, + /// Duplicate bridge request DuplicateRequest, + /// Gas limit exceeded GasLimitExceeded, } + impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Error::Unauthorized => write!(f, "Caller is not authorized"), + Error::TokenNotFound => write!(f, "Token does not exist"), + Error::InvalidChain => write!(f, "Invalid chain ID"), + Error::BridgeNotSupported => write!(f, "Bridge not supported for this token"), + Error::InsufficientSignatures => write!(f, "Insufficient signatures collected"), + Error::RequestExpired => write!(f, "Bridge request has expired"), + Error::AlreadySigned => write!(f, "Already signed this request"), + Error::InvalidRequest => write!(f, "Invalid bridge request"), + Error::BridgePaused => write!(f, "Bridge operations are paused"), + Error::InvalidMetadata => write!(f, "Invalid metadata"), + Error::DuplicateRequest => write!(f, "Duplicate bridge request"), + Error::GasLimitExceeded => write!(f, "Gas limit exceeded"), + } + } + } + + impl ContractError for Error { + fn error_code(&self) -> u32 { + match self { + Error::Unauthorized => bridge_codes::BRIDGE_UNAUTHORIZED, + Error::TokenNotFound => bridge_codes::BRIDGE_TOKEN_NOT_FOUND, + Error::InvalidChain => bridge_codes::BRIDGE_INVALID_CHAIN, + Error::BridgeNotSupported => bridge_codes::BRIDGE_NOT_SUPPORTED, + Error::InsufficientSignatures => bridge_codes::BRIDGE_INSUFFICIENT_SIGNATURES, + Error::RequestExpired => bridge_codes::BRIDGE_REQUEST_EXPIRED, + Error::AlreadySigned => bridge_codes::BRIDGE_ALREADY_SIGNED, + Error::InvalidRequest => bridge_codes::BRIDGE_INVALID_REQUEST, + Error::BridgePaused => bridge_codes::BRIDGE_PAUSED, + Error::InvalidMetadata => bridge_codes::BRIDGE_INVALID_METADATA, + Error::DuplicateRequest => bridge_codes::BRIDGE_DUPLICATE_REQUEST, + Error::GasLimitExceeded => bridge_codes::BRIDGE_GAS_LIMIT_EXCEEDED, + } + } + + fn error_description(&self) -> &'static str { + match self { + Error::Unauthorized => "Caller does not have permission to perform this operation", + Error::TokenNotFound => "The specified token does not exist", + Error::InvalidChain => "The destination chain ID is invalid", + Error::BridgeNotSupported => "Cross-chain bridging is not supported for this token", + Error::InsufficientSignatures => "Not enough signatures collected for bridge operation", + Error::RequestExpired => "The bridge request has expired and can no longer be executed", + Error::AlreadySigned => "You have already signed this bridge request", + Error::InvalidRequest => "The bridge request is invalid or malformed", + Error::BridgePaused => "Bridge operations are temporarily paused", + Error::InvalidMetadata => "The token metadata is invalid", + Error::DuplicateRequest => "A bridge request with these parameters already exists", + Error::GasLimitExceeded => "The operation exceeded the gas limit", + } + } + + fn error_category(&self) -> ErrorCategory { + ErrorCategory::Bridge + } + } + /// Bridge contract for cross-chain property token transfers #[ink(storage)] pub struct PropertyBridge { diff --git a/contracts/compliance_registry/lib.rs b/contracts/compliance_registry/lib.rs index b8c30eeb9..8cf837724 100644 --- a/contracts/compliance_registry/lib.rs +++ b/contracts/compliance_registry/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] use propchain_traits::ComplianceChecker; +use propchain_traits::*; #[ink::contract] mod compliance_registry { @@ -244,19 +245,86 @@ mod compliance_registry { #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum Error { + /// Caller is not authorized NotAuthorized, + /// User is not verified NotVerified, + /// Verification has expired VerificationExpired, + /// User has high risk level HighRisk, + /// Jurisdiction is prohibited ProhibitedJurisdiction, + /// User already verified AlreadyVerified, + /// Consent not given ConsentNotGiven, + /// Data retention period expired DataRetentionExpired, + /// Invalid risk score InvalidRiskScore, + /// Invalid document type InvalidDocumentType, + /// Jurisdiction not supported JurisdictionNotSupported, } + impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Error::NotAuthorized => write!(f, "Caller is not authorized"), + Error::NotVerified => write!(f, "User is not verified"), + Error::VerificationExpired => write!(f, "Verification has expired"), + Error::HighRisk => write!(f, "User has high risk level"), + Error::ProhibitedJurisdiction => write!(f, "Jurisdiction is prohibited"), + Error::AlreadyVerified => write!(f, "User already verified"), + Error::ConsentNotGiven => write!(f, "Consent not given"), + Error::DataRetentionExpired => write!(f, "Data retention period expired"), + Error::InvalidRiskScore => write!(f, "Invalid risk score"), + Error::InvalidDocumentType => write!(f, "Invalid document type"), + Error::JurisdictionNotSupported => write!(f, "Jurisdiction not supported"), + } + } + } + + impl ContractError for Error { + fn error_code(&self) -> u32 { + match self { + Error::NotAuthorized => propchain_traits::errors::compliance_codes::COMPLIANCE_UNAUTHORIZED, + Error::NotVerified => propchain_traits::errors::compliance_codes::COMPLIANCE_NOT_VERIFIED, + Error::VerificationExpired => propchain_traits::errors::compliance_codes::COMPLIANCE_EXPIRED, + Error::HighRisk => propchain_traits::errors::compliance_codes::COMPLIANCE_CHECK_FAILED, + Error::ProhibitedJurisdiction => propchain_traits::errors::compliance_codes::COMPLIANCE_CHECK_FAILED, + Error::AlreadyVerified => propchain_traits::errors::compliance_codes::COMPLIANCE_UNAUTHORIZED, + Error::ConsentNotGiven => propchain_traits::errors::compliance_codes::COMPLIANCE_NOT_VERIFIED, + Error::DataRetentionExpired => propchain_traits::errors::compliance_codes::COMPLIANCE_EXPIRED, + Error::InvalidRiskScore => propchain_traits::errors::compliance_codes::COMPLIANCE_CHECK_FAILED, + Error::InvalidDocumentType => propchain_traits::errors::compliance_codes::COMPLIANCE_DOCUMENT_MISSING, + Error::JurisdictionNotSupported => propchain_traits::errors::compliance_codes::COMPLIANCE_CHECK_FAILED, + } + } + + fn error_description(&self) -> &'static str { + match self { + Error::NotAuthorized => "Caller does not have permission to perform this operation", + Error::NotVerified => "The user has not completed verification", + Error::VerificationExpired => "The user's verification has expired and needs renewal", + Error::HighRisk => "The user has been assessed as high risk", + Error::ProhibitedJurisdiction => "The user's jurisdiction is prohibited", + Error::AlreadyVerified => "The user is already verified", + Error::ConsentNotGiven => "The user has not provided required consent", + Error::DataRetentionExpired => "The data retention period has expired", + Error::InvalidRiskScore => "The risk score is invalid or out of range", + Error::InvalidDocumentType => "The document type is invalid or not supported", + Error::JurisdictionNotSupported => "The jurisdiction is not supported", + } + } + + fn error_category(&self) -> ErrorCategory { + ErrorCategory::Compliance + } + } + pub type Result = core::result::Result; /// Events diff --git a/contracts/escrow/src/lib.rs b/contracts/escrow/src/lib.rs index a237c8c13..afdc703df 100644 --- a/contracts/escrow/src/lib.rs +++ b/contracts/escrow/src/lib.rs @@ -5,6 +5,7 @@ use ink::storage::Mapping; #[cfg(not(feature = "std"))] use scale_info::prelude::{string::String, vec::Vec}; +use propchain_traits::*; pub mod tests; @@ -16,21 +17,96 @@ mod propchain_escrow { #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum Error { + /// Escrow does not exist EscrowNotFound, + /// Caller is not authorized Unauthorized, + /// Invalid escrow status for operation InvalidStatus, + /// Insufficient funds in escrow InsufficientFunds, + /// Required conditions not met ConditionsNotMet, + /// Signature threshold not reached SignatureThresholdNotMet, + /// Already signed this request AlreadySigned, + /// Document does not exist DocumentNotFound, + /// Dispute is currently active DisputeActive, + /// Time lock period still active TimeLockActive, + /// Invalid configuration parameters InvalidConfiguration, + /// Escrow already funded EscrowAlreadyFunded, + /// Participant not found ParticipantNotFound, } + impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Error::EscrowNotFound => write!(f, "Escrow does not exist"), + Error::Unauthorized => write!(f, "Caller is not authorized"), + Error::InvalidStatus => write!(f, "Invalid escrow status for operation"), + Error::InsufficientFunds => write!(f, "Insufficient funds in escrow"), + Error::ConditionsNotMet => write!(f, "Required conditions not met"), + Error::SignatureThresholdNotMet => write!(f, "Signature threshold not reached"), + Error::AlreadySigned => write!(f, "Already signed this request"), + Error::DocumentNotFound => write!(f, "Document does not exist"), + Error::DisputeActive => write!(f, "Dispute is currently active"), + Error::TimeLockActive => write!(f, "Time lock period still active"), + Error::InvalidConfiguration => write!(f, "Invalid configuration parameters"), + Error::EscrowAlreadyFunded => write!(f, "Escrow already funded"), + Error::ParticipantNotFound => write!(f, "Participant not found"), + } + } + } + + impl ContractError for Error { + fn error_code(&self) -> u32 { + match self { + Error::EscrowNotFound => propchain_traits::errors::escrow_codes::ESCROW_NOT_FOUND, + Error::Unauthorized => propchain_traits::errors::escrow_codes::UNAUTHORIZED_ACCESS, + Error::InvalidStatus => propchain_traits::errors::escrow_codes::INVALID_STATUS, + Error::InsufficientFunds => propchain_traits::errors::escrow_codes::INSUFFICIENT_ESCROW_FUNDS, + Error::ConditionsNotMet => propchain_traits::errors::escrow_codes::CONDITIONS_NOT_MET, + Error::SignatureThresholdNotMet => propchain_traits::errors::escrow_codes::SIGNATURE_THRESHOLD_NOT_MET, + Error::AlreadySigned => propchain_traits::errors::escrow_codes::ALREADY_SIGNED_ESCROW, + Error::DocumentNotFound => propchain_traits::errors::escrow_codes::DOCUMENT_NOT_FOUND, + Error::DisputeActive => propchain_traits::errors::escrow_codes::DISPUTE_ACTIVE, + Error::TimeLockActive => propchain_traits::errors::escrow_codes::TIME_LOCK_ACTIVE, + Error::InvalidConfiguration => propchain_traits::errors::escrow_codes::INVALID_CONFIGURATION, + Error::EscrowAlreadyFunded => propchain_traits::errors::escrow_codes::ESCROW_ALREADY_FUNDED, + Error::ParticipantNotFound => propchain_traits::errors::escrow_codes::PARTICIPANT_NOT_FOUND, + } + } + + fn error_description(&self) -> &'static str { + match self { + Error::EscrowNotFound => "The specified escrow does not exist", + Error::Unauthorized => "Caller does not have permission to perform this operation", + Error::InvalidStatus => "The escrow is not in the required state for this operation", + Error::InsufficientFunds => "The escrow does not have sufficient funds", + Error::ConditionsNotMet => "Not all required conditions have been met", + Error::SignatureThresholdNotMet => "Insufficient signatures collected", + Error::AlreadySigned => "You have already signed this request", + Error::DocumentNotFound => "The requested document does not exist", + Error::DisputeActive => "A dispute is currently active on this escrow", + Error::TimeLockActive => "The time lock period has not yet expired", + Error::InvalidConfiguration => "The escrow configuration is invalid", + Error::EscrowAlreadyFunded => "This escrow has already been funded", + Error::ParticipantNotFound => "The specified participant is not in the escrow", + } + } + + fn error_category(&self) -> ErrorCategory { + ErrorCategory::Escrow + } + } + /// Escrow status enumeration #[derive(Debug, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] diff --git a/contracts/fees/src/lib.rs b/contracts/fees/src/lib.rs index e67022d90..151fcbd01 100644 --- a/contracts/fees/src/lib.rs +++ b/contracts/fees/src/lib.rs @@ -6,6 +6,7 @@ use ink::prelude::vec::Vec; use ink::storage::Mapping; use propchain_traits::DynamicFeeProvider; use propchain_traits::FeeOperation; +use propchain_traits::*; /// Dynamic Fee and Market Mechanism contract for PropChain. /// Implements congestion-based fees, premium listing auctions, validator incentives, @@ -144,16 +145,71 @@ mod propchain_fees { #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum FeeError { + /// Caller is not authorized Unauthorized, + /// Auction does not exist AuctionNotFound, + /// Auction has ended AuctionEnded, + /// Auction has not ended yet AuctionNotEnded, + /// Bid amount is too low BidTooLow, + /// Auction already settled AlreadySettled, + /// Invalid configuration InvalidConfig, + /// Invalid property ID InvalidProperty, } + impl core::fmt::Display for FeeError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + FeeError::Unauthorized => write!(f, "Caller is not authorized"), + FeeError::AuctionNotFound => write!(f, "Auction does not exist"), + FeeError::AuctionEnded => write!(f, "Auction has ended"), + FeeError::AuctionNotEnded => write!(f, "Auction has not ended yet"), + FeeError::BidTooLow => write!(f, "Bid amount is too low"), + FeeError::AlreadySettled => write!(f, "Auction already settled"), + FeeError::InvalidConfig => write!(f, "Invalid configuration"), + FeeError::InvalidProperty => write!(f, "Invalid property ID"), + } + } + } + + impl ContractError for FeeError { + fn error_code(&self) -> u32 { + match self { + FeeError::Unauthorized => propchain_traits::errors::fee_codes::FEE_UNAUTHORIZED, + FeeError::AuctionNotFound => propchain_traits::errors::fee_codes::FEE_AUCTION_NOT_FOUND, + FeeError::AuctionEnded => propchain_traits::errors::fee_codes::FEE_AUCTION_ENDED, + FeeError::AuctionNotEnded => propchain_traits::errors::fee_codes::FEE_AUCTION_NOT_ENDED, + FeeError::BidTooLow => propchain_traits::errors::fee_codes::FEE_BID_TOO_LOW, + FeeError::AlreadySettled => propchain_traits::errors::fee_codes::FEE_ALREADY_SETTLED, + FeeError::InvalidConfig => propchain_traits::errors::fee_codes::FEE_INVALID_CONFIG, + FeeError::InvalidProperty => propchain_traits::errors::fee_codes::FEE_INVALID_PROPERTY, + } + } + + fn error_description(&self) -> &'static str { + match self { + FeeError::Unauthorized => "Caller does not have permission to perform this operation", + FeeError::AuctionNotFound => "The specified auction does not exist", + FeeError::AuctionEnded => "This auction has already ended", + FeeError::AuctionNotEnded => "The auction is still active and has not ended", + FeeError::BidTooLow => "The bid amount is below the minimum required", + FeeError::AlreadySettled => "This auction has already been settled", + FeeError::InvalidConfig => "The fee configuration is invalid", + FeeError::InvalidProperty => "The property ID is invalid or does not exist", + } + } + + fn error_category(&self) -> ErrorCategory { + ErrorCategory::Fees + } + } + #[ink(storage)] pub struct FeeManager { admin: AccountId, diff --git a/contracts/property-token/src/lib.rs b/contracts/property-token/src/lib.rs index b392a6b77..39432dc39 100644 --- a/contracts/property-token/src/lib.rs +++ b/contracts/property-token/src/lib.rs @@ -16,34 +16,153 @@ mod property_token { #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum Error { // Standard ERC errors + /// Token does not exist TokenNotFound, + /// Caller is not authorized Unauthorized, // Property-specific errors + /// Property does not exist PropertyNotFound, + /// Metadata is invalid or malformed InvalidMetadata, + /// Document does not exist DocumentNotFound, + /// Compliance check failed ComplianceFailed, // Cross-chain bridge errors + /// Bridge functionality not supported BridgeNotSupported, + /// Invalid chain ID InvalidChain, + /// Token is locked in bridge BridgeLocked, + /// Insufficient signatures for bridge operation InsufficientSignatures, + /// Bridge request has expired RequestExpired, + /// Invalid bridge request InvalidRequest, + /// Bridge operations are paused BridgePaused, + /// Gas limit exceeded GasLimitExceeded, + /// Metadata is corrupted MetadataCorruption, + /// Invalid bridge operator InvalidBridgeOperator, + /// Duplicate bridge request DuplicateBridgeRequest, + /// Bridge operation timed out BridgeTimeout, + /// Already signed this request AlreadySigned, + /// Insufficient balance InsufficientBalance, + /// Invalid amount InvalidAmount, + /// Proposal not found ProposalNotFound, + /// Proposal is closed ProposalClosed, + /// Ask not found AskNotFound, } + impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Error::TokenNotFound => write!(f, "Token does not exist"), + Error::Unauthorized => write!(f, "Caller is not authorized"), + Error::PropertyNotFound => write!(f, "Property does not exist"), + Error::InvalidMetadata => write!(f, "Metadata is invalid or malformed"), + Error::DocumentNotFound => write!(f, "Document does not exist"), + Error::ComplianceFailed => write!(f, "Compliance check failed"), + Error::BridgeNotSupported => write!(f, "Bridge functionality not supported"), + Error::InvalidChain => write!(f, "Invalid chain ID"), + Error::BridgeLocked => write!(f, "Token is locked in bridge"), + Error::InsufficientSignatures => write!(f, "Insufficient signatures for bridge operation"), + Error::RequestExpired => write!(f, "Bridge request has expired"), + Error::InvalidRequest => write!(f, "Invalid bridge request"), + Error::BridgePaused => write!(f, "Bridge operations are paused"), + Error::GasLimitExceeded => write!(f, "Gas limit exceeded"), + Error::MetadataCorruption => write!(f, "Metadata is corrupted"), + Error::InvalidBridgeOperator => write!(f, "Invalid bridge operator"), + Error::DuplicateBridgeRequest => write!(f, "Duplicate bridge request"), + Error::BridgeTimeout => write!(f, "Bridge operation timed out"), + Error::AlreadySigned => write!(f, "Already signed this request"), + Error::InsufficientBalance => write!(f, "Insufficient balance"), + Error::InvalidAmount => write!(f, "Invalid amount"), + Error::ProposalNotFound => write!(f, "Proposal not found"), + Error::ProposalClosed => write!(f, "Proposal is closed"), + Error::AskNotFound => write!(f, "Ask not found"), + } + } + } + + impl ContractError for Error { + fn error_code(&self) -> u32 { + match self { + Error::TokenNotFound => property_token_codes::TOKEN_NOT_FOUND, + Error::Unauthorized => property_token_codes::UNAUTHORIZED_TRANSFER, + Error::PropertyNotFound => property_token_codes::PROPERTY_NOT_FOUND, + Error::InvalidMetadata => property_token_codes::INVALID_METADATA, + Error::DocumentNotFound => property_token_codes::DOCUMENT_NOT_FOUND, + Error::ComplianceFailed => property_token_codes::COMPLIANCE_FAILED, + Error::BridgeNotSupported => property_token_codes::BRIDGE_NOT_SUPPORTED, + Error::InvalidChain => property_token_codes::INVALID_CHAIN, + Error::BridgeLocked => property_token_codes::BRIDGE_LOCKED, + Error::InsufficientSignatures => property_token_codes::INSUFFICIENT_SIGNATURES, + Error::RequestExpired => property_token_codes::REQUEST_EXPIRED, + Error::InvalidRequest => property_token_codes::INVALID_REQUEST, + Error::BridgePaused => property_token_codes::BRIDGE_PAUSED, + Error::GasLimitExceeded => property_token_codes::GAS_LIMIT_EXCEEDED, + Error::MetadataCorruption => property_token_codes::METADATA_CORRUPTION, + Error::InvalidBridgeOperator => property_token_codes::INVALID_BRIDGE_OPERATOR, + Error::DuplicateBridgeRequest => property_token_codes::DUPLICATE_BRIDGE_REQUEST, + Error::BridgeTimeout => property_token_codes::BRIDGE_TIMEOUT, + Error::AlreadySigned => property_token_codes::ALREADY_SIGNED, + Error::InsufficientBalance => property_token_codes::INSUFFICIENT_BALANCE, + Error::InvalidAmount => property_token_codes::INVALID_AMOUNT, + Error::ProposalNotFound => property_token_codes::PROPOSAL_NOT_FOUND, + Error::ProposalClosed => property_token_codes::PROPOSAL_CLOSED, + Error::AskNotFound => property_token_codes::ASK_NOT_FOUND, + } + } + + fn error_description(&self) -> &'static str { + match self { + Error::TokenNotFound => "The specified token does not exist", + Error::Unauthorized => "Caller does not have permission to perform this operation", + Error::PropertyNotFound => "The specified property does not exist", + Error::InvalidMetadata => "The provided metadata is invalid or malformed", + Error::DocumentNotFound => "The requested document does not exist", + Error::ComplianceFailed => "The operation failed compliance verification", + Error::BridgeNotSupported => "Cross-chain bridging is not supported for this token", + Error::InvalidChain => "The destination chain ID is invalid", + Error::BridgeLocked => "The token is currently locked in a bridge operation", + Error::InsufficientSignatures => "Not enough signatures collected for bridge operation", + Error::RequestExpired => "The bridge request has expired and can no longer be executed", + Error::InvalidRequest => "The bridge request is invalid or malformed", + Error::BridgePaused => "Bridge operations are temporarily paused", + Error::GasLimitExceeded => "The operation exceeded the gas limit", + Error::MetadataCorruption => "The token metadata has been corrupted", + Error::InvalidBridgeOperator => "The bridge operator is not authorized", + Error::DuplicateBridgeRequest => "A bridge request with these parameters already exists", + Error::BridgeTimeout => "The bridge operation timed out", + Error::AlreadySigned => "You have already signed this bridge request", + Error::InsufficientBalance => "Account has insufficient balance", + Error::InvalidAmount => "The amount is invalid or out of range", + Error::ProposalNotFound => "The governance proposal does not exist", + Error::ProposalClosed => "The governance proposal is closed for voting", + Error::AskNotFound => "The sell ask does not exist", + } + } + + fn error_category(&self) -> ErrorCategory { + ErrorCategory::PropertyToken + } + } + /// Property Token contract that maintains compatibility with ERC-721 and ERC-1155 /// while adding real estate-specific features and cross-chain support #[ink(storage)] diff --git a/contracts/traits/src/errors.rs b/contracts/traits/src/errors.rs new file mode 100644 index 000000000..f089fb20f --- /dev/null +++ b/contracts/traits/src/errors.rs @@ -0,0 +1,258 @@ +//! Shared error handling framework for PropChain contracts +//! +//! This module provides a unified error handling system with: +//! - Base error trait that all contract errors implement +//! - Common error variants reusable across contracts +//! - Numeric error codes for external API integration +//! - Full Debug, Display, and From trait implementations + +use core::fmt; +use scale::{Decode, Encode}; + +#[cfg(feature = "std")] +use scale_info::TypeInfo; + +/// ============================================================================= +/// Base Error Trait +/// ============================================================================= + +/// Base trait for all PropChain contract errors. +/// All contract-specific error enums must implement this trait. +pub trait ContractError: fmt::Debug + fmt::Display + Encode + Decode { + /// Returns the numeric error code for this error variant. + /// Used for external API integration and monitoring. + fn error_code(&self) -> u32; + + /// Returns a human-readable description of the error. + fn error_description(&self) -> &'static str; + + /// Returns the category of this error. + fn error_category(&self) -> ErrorCategory { + match self.error_code() { + 1..=999 => ErrorCategory::Common, + 1000..=1999 => ErrorCategory::PropertyToken, + 2000..=2999 => ErrorCategory::Escrow, + 3000..=3999 => ErrorCategory::Bridge, + 4000..=4999 => ErrorCategory::Oracle, + 5000..=5999 => ErrorCategory::Fees, + 6000..=6999 => ErrorCategory::Compliance, + _ => ErrorCategory::Unknown, + } + } +} + +/// Error categories for classification and monitoring +#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(TypeInfo))] +pub enum ErrorCategory { + Common, + PropertyToken, + Escrow, + Bridge, + Oracle, + Fees, + Compliance, + Unknown, +} + +impl fmt::Display for ErrorCategory { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ErrorCategory::Common => write!(f, "Common"), + ErrorCategory::PropertyToken => write!(f, "PropertyToken"), + ErrorCategory::Escrow => write!(f, "Escrow"), + ErrorCategory::Bridge => write!(f, "Bridge"), + ErrorCategory::Oracle => write!(f, "Oracle"), + ErrorCategory::Fees => write!(f, "Fees"), + ErrorCategory::Compliance => write!(f, "Compliance"), + ErrorCategory::Unknown => write!(f, "Unknown"), + } + } +} + +/// ============================================================================= +/// Common Error Variants +/// ============================================================================= + +/// Common error variants that can be used across multiple contracts +#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(TypeInfo))] +pub enum CommonError { + /// Unauthorized access - caller lacks required permissions + Unauthorized = 1, + /// Invalid parameters provided to function + InvalidParameters = 2, + /// Resource not found (generic) + NotFound = 3, + /// Insufficient funds or balance + InsufficientFunds = 4, + /// Operation not allowed in current state + InvalidState = 5, + /// Internal contract error + InternalError = 6, + /// Serialization/deserialization error + CodecError = 7, + /// Feature not yet implemented + NotImplemented = 8, + /// Operation timed out + Timeout = 9, + /// Duplicate operation or resource + Duplicate = 10, +} + +impl fmt::Display for CommonError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CommonError::Unauthorized => write!(f, "Unauthorized: caller lacks required permissions"), + CommonError::InvalidParameters => write!(f, "Invalid parameters provided to function"), + CommonError::NotFound => write!(f, "Resource not found"), + CommonError::InsufficientFunds => write!(f, "Insufficient funds or balance"), + CommonError::InvalidState => write!(f, "Operation not allowed in current state"), + CommonError::InternalError => write!(f, "Internal contract error occurred"), + CommonError::CodecError => write!(f, "Serialization/deserialization error"), + CommonError::NotImplemented => write!(f, "Feature not yet implemented"), + CommonError::Timeout => write!(f, "Operation timed out"), + CommonError::Duplicate => write!(f, "Duplicate operation or resource"), + } + } +} + +impl ContractError for CommonError { + fn error_code(&self) -> u32 { + *self as u32 + } + + fn error_description(&self) -> &'static str { + match self { + CommonError::Unauthorized => "Caller does not have permission to perform this operation", + CommonError::InvalidParameters => "One or more function parameters are invalid", + CommonError::NotFound => "The requested resource does not exist", + CommonError::InsufficientFunds => "Account has insufficient balance for this operation", + CommonError::InvalidState => "Cannot perform this operation in the current state", + CommonError::InternalError => "An internal error occurred in the contract", + CommonError::CodecError => "Failed to encode or decode data", + CommonError::NotImplemented => "This feature is not yet implemented", + CommonError::Timeout => "The operation exceeded its time limit", + CommonError::Duplicate => "This operation or resource already exists", + } + } + + fn error_category(&self) -> ErrorCategory { + ErrorCategory::Common + } +} + +/// ============================================================================= +/// Error Code Constants +/// ============================================================================= + +/// Common error codes (1-999) +pub mod common_codes { + pub const UNAUTHORIZED: u32 = 1; + pub const INVALID_PARAMETERS: u32 = 2; + pub const NOT_FOUND: u32 = 3; + pub const INSUFFICIENT_FUNDS: u32 = 4; + pub const INVALID_STATE: u32 = 5; + pub const INTERNAL_ERROR: u32 = 6; + pub const CODEC_ERROR: u32 = 7; + pub const NOT_IMPLEMENTED: u32 = 8; + pub const TIMEOUT: u32 = 9; + pub const DUPLICATE: u32 = 10; +} + +/// PropertyToken error codes (1000-1999) +pub mod property_token_codes { + pub const TOKEN_NOT_FOUND: u32 = 1001; + pub const UNAUTHORIZED_TRANSFER: u32 = 1002; + pub const PROPERTY_NOT_FOUND: u32 = 1003; + pub const INVALID_METADATA: u32 = 1004; + pub const DOCUMENT_NOT_FOUND: u32 = 1005; + pub const COMPLIANCE_FAILED: u32 = 1006; + pub const BRIDGE_NOT_SUPPORTED: u32 = 1007; + pub const INVALID_CHAIN: u32 = 1008; + pub const BRIDGE_LOCKED: u32 = 1009; + pub const INSUFFICIENT_SIGNATURES: u32 = 1010; + pub const REQUEST_EXPIRED: u32 = 1011; + pub const INVALID_REQUEST: u32 = 1012; + pub const BRIDGE_PAUSED: u32 = 1013; + pub const GAS_LIMIT_EXCEEDED: u32 = 1014; + pub const METADATA_CORRUPTION: u32 = 1015; + pub const INVALID_BRIDGE_OPERATOR: u32 = 1016; + pub const DUPLICATE_BRIDGE_REQUEST: u32 = 1017; + pub const BRIDGE_TIMEOUT: u32 = 1018; + pub const ALREADY_SIGNED: u32 = 1019; + pub const INSUFFICIENT_BALANCE: u32 = 1020; + pub const INVALID_AMOUNT: u32 = 1021; + pub const PROPOSAL_NOT_FOUND: u32 = 1022; + pub const PROPOSAL_CLOSED: u32 = 1023; + pub const ASK_NOT_FOUND: u32 = 1024; +} + +/// Escrow error codes (2000-2999) +pub mod escrow_codes { + pub const ESCROW_NOT_FOUND: u32 = 2001; + pub const UNAUTHORIZED_ACCESS: u32 = 2002; + pub const INVALID_STATUS: u32 = 2003; + pub const INSUFFICIENT_ESCROW_FUNDS: u32 = 2004; + pub const CONDITIONS_NOT_MET: u32 = 2005; + pub const SIGNATURE_THRESHOLD_NOT_MET: u32 = 2006; + pub const ALREADY_SIGNED_ESCROW: u32 = 2007; + pub const DOCUMENT_NOT_FOUND: u32 = 2008; + pub const DISPUTE_ACTIVE: u32 = 2009; + pub const TIME_LOCK_ACTIVE: u32 = 2010; + pub const INVALID_CONFIGURATION: u32 = 2011; + pub const ESCROW_ALREADY_FUNDED: u32 = 2012; + pub const PARTICIPANT_NOT_FOUND: u32 = 2013; +} + +/// Bridge error codes (3000-3999) +pub mod bridge_codes { + pub const BRIDGE_UNAUTHORIZED: u32 = 3001; + pub const BRIDGE_TOKEN_NOT_FOUND: u32 = 3002; + pub const BRIDGE_INVALID_CHAIN: u32 = 3003; + pub const BRIDGE_NOT_SUPPORTED: u32 = 3004; + pub const BRIDGE_INSUFFICIENT_SIGNATURES: u32 = 3005; + pub const BRIDGE_REQUEST_EXPIRED: u32 = 3006; + pub const BRIDGE_ALREADY_SIGNED: u32 = 3007; + pub const BRIDGE_INVALID_REQUEST: u32 = 3008; + pub const BRIDGE_PAUSED: u32 = 3009; + pub const BRIDGE_INVALID_METADATA: u32 = 3010; + pub const BRIDGE_DUPLICATE_REQUEST: u32 = 3011; + pub const BRIDGE_GAS_LIMIT_EXCEEDED: u32 = 3012; +} + +/// Oracle error codes (4000-4999) +pub mod oracle_codes { + pub const ORACLE_PROPERTY_NOT_FOUND: u32 = 4001; + pub const ORACLE_INSUFFICIENT_SOURCES: u32 = 4002; + pub const ORACLE_INVALID_VALUATION: u32 = 4003; + pub const ORACLE_UNAUTHORIZED: u32 = 4004; + pub const ORACLE_SOURCE_NOT_FOUND: u32 = 4005; + pub const ORACLE_INVALID_PARAMETERS: u32 = 4006; + pub const ORACLE_PRICE_FEED_ERROR: u32 = 4007; + pub const ORACLE_ALERT_NOT_FOUND: u32 = 4008; + pub const ORACLE_INSUFFICIENT_REPUTATION: u32 = 4009; + pub const ORACLE_SOURCE_ALREADY_EXISTS: u32 = 4010; + pub const ORACLE_REQUEST_PENDING: u32 = 4011; +} + +/// Fee error codes (5000-5999) +pub mod fee_codes { + pub const FEE_UNAUTHORIZED: u32 = 5001; + pub const FEE_AUCTION_NOT_FOUND: u32 = 5002; + pub const FEE_AUCTION_ENDED: u32 = 5003; + pub const FEE_AUCTION_NOT_ENDED: u32 = 5004; + pub const FEE_BID_TOO_LOW: u32 = 5005; + pub const FEE_ALREADY_SETTLED: u32 = 5006; + pub const FEE_INVALID_CONFIG: u32 = 5007; + pub const FEE_INVALID_PROPERTY: u32 = 5008; +} + +/// Compliance error codes (6000-6999) +pub mod compliance_codes { + pub const COMPLIANCE_UNAUTHORIZED: u32 = 6001; + pub const COMPLIANCE_NOT_VERIFIED: u32 = 6002; + pub const COMPLIANCE_CHECK_FAILED: u32 = 6003; + pub const COMPLIANCE_DOCUMENT_MISSING: u32 = 6004; + pub const COMPLIANCE_EXPIRED: u32 = 6005; +} diff --git a/contracts/traits/src/lib.rs b/contracts/traits/src/lib.rs index 922939ed1..fd13e55b2 100644 --- a/contracts/traits/src/lib.rs +++ b/contracts/traits/src/lib.rs @@ -1,25 +1,95 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod errors; + use ink::prelude::string::String; use ink::primitives::AccountId; +pub use errors::*; /// Error types for the Property Valuation Oracle #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum OracleError { + /// Property not found in the oracle system PropertyNotFound, + /// Insufficient oracle sources available InsufficientSources, + /// Valuation data is invalid or out of range InvalidValuation, + /// Caller is not authorized to perform this operation Unauthorized, + /// Oracle source does not exist OracleSourceNotFound, + /// Invalid parameters provided InvalidParameters, + /// Error from external price feed PriceFeedError, + /// Price alert not found AlertNotFound, + /// Oracle source has insufficient reputation InsufficientReputation, + /// Oracle source already registered SourceAlreadyExists, + /// Valuation request is still pending RequestPending, } +impl core::fmt::Display for OracleError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + OracleError::PropertyNotFound => write!(f, "Property not found in the oracle system"), + OracleError::InsufficientSources => write!(f, "Insufficient oracle sources available"), + OracleError::InvalidValuation => write!(f, "Valuation data is invalid or out of range"), + OracleError::Unauthorized => write!(f, "Caller is not authorized to perform this operation"), + OracleError::OracleSourceNotFound => write!(f, "Oracle source does not exist"), + OracleError::InvalidParameters => write!(f, "Invalid parameters provided"), + OracleError::PriceFeedError => write!(f, "Error from external price feed"), + OracleError::AlertNotFound => write!(f, "Price alert not found"), + OracleError::InsufficientReputation => write!(f, "Oracle source has insufficient reputation"), + OracleError::SourceAlreadyExists => write!(f, "Oracle source already registered"), + OracleError::RequestPending => write!(f, "Valuation request is still pending"), + } + } +} + +impl ContractError for OracleError { + fn error_code(&self) -> u32 { + match self { + OracleError::PropertyNotFound => oracle_codes::ORACLE_PROPERTY_NOT_FOUND, + OracleError::InsufficientSources => oracle_codes::ORACLE_INSUFFICIENT_SOURCES, + OracleError::InvalidValuation => oracle_codes::ORACLE_INVALID_VALUATION, + OracleError::Unauthorized => oracle_codes::ORACLE_UNAUTHORIZED, + OracleError::OracleSourceNotFound => oracle_codes::ORACLE_SOURCE_NOT_FOUND, + OracleError::InvalidParameters => oracle_codes::ORACLE_INVALID_PARAMETERS, + OracleError::PriceFeedError => oracle_codes::ORACLE_PRICE_FEED_ERROR, + OracleError::AlertNotFound => oracle_codes::ORACLE_ALERT_NOT_FOUND, + OracleError::InsufficientReputation => oracle_codes::ORACLE_INSUFFICIENT_REPUTATION, + OracleError::SourceAlreadyExists => oracle_codes::ORACLE_SOURCE_ALREADY_EXISTS, + OracleError::RequestPending => oracle_codes::ORACLE_REQUEST_PENDING, + } + } + + fn error_description(&self) -> &'static str { + match self { + OracleError::PropertyNotFound => "The requested property does not exist in the oracle system", + OracleError::InsufficientSources => "Not enough oracle sources are available to provide a reliable valuation", + OracleError::InvalidValuation => "The valuation data is invalid, zero, or out of acceptable range", + OracleError::Unauthorized => "Caller does not have permission to perform this operation", + OracleError::OracleSourceNotFound => "The specified oracle source does not exist", + OracleError::InvalidParameters => "One or more function parameters are invalid", + OracleError::PriceFeedError => "Failed to retrieve data from external price feed", + OracleError::AlertNotFound => "The requested price alert does not exist", + OracleError::InsufficientReputation => "Oracle source reputation is below required threshold", + OracleError::SourceAlreadyExists => "An oracle source with this identifier already exists", + OracleError::RequestPending => "A valuation request for this property is already pending", + } + } + + fn error_category(&self) -> ErrorCategory { + ErrorCategory::Oracle + } +} + /// Trait definitions for PropChain contracts pub trait PropertyRegistry { /// Error type for the contract