Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "koii-contract-error-handling"
version = "0.1.0"
edition = "2021"

[dependencies]
thiserror = "1.0"

[dev-dependencies]
rstest = "0.18.2"
74 changes: 74 additions & 0 deletions src/errors/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use thiserror::Error;

/// Comprehensive error enum for smart contract operations
#[derive(Error, Debug, Clone, PartialEq)]
pub enum ContractError {
/// Generic initialization error
#[error("Initialization failed: {0}")]
InitializationError(String),

/// Token transfer validation error
#[error("Invalid token transfer: {0}")]
TokenTransferError(String),

/// Authorization and permission-related errors
#[error("Authorization failed: {0}")]
AuthorizationError(String),

/// Validation errors for input parameters
#[error("Invalid input: {0}")]
ValidationError(String),

/// Resource not found errors
#[error("Resource not found: {0}")]
ResourceNotFoundError(String),

/// Arithmetic or computational errors
#[error("Computation error: {0}")]
ComputationError(String),

/// Generic unexpected error
#[error("Unexpected error occurred: {0}")]
UnexpectedError(String),
}

/// Result type using our custom ContractError
pub type ContractResult<T> = Result<T, ContractError>;

/// Utility functions for error handling
impl ContractError {
/// Create a new initialization error
pub fn initialization(message: impl ToString) -> Self {
ContractError::InitializationError(message.to_string())
}

/// Create a new token transfer error
pub fn token_transfer(message: impl ToString) -> Self {
ContractError::TokenTransferError(message.to_string())
}

/// Create a new authorization error
pub fn authorization(message: impl ToString) -> Self {
ContractError::AuthorizationError(message.to_string())
}

/// Create a new validation error
pub fn validation(message: impl ToString) -> Self {
ContractError::ValidationError(message.to_string())
}

/// Create a new resource not found error
pub fn resource_not_found(message: impl ToString) -> Self {
ContractError::ResourceNotFoundError(message.to_string())
}

/// Create a new computation error
pub fn computation(message: impl ToString) -> Self {
ContractError::ComputationError(message.to_string())
}

/// Create a new unexpected error
pub fn unexpected(message: impl ToString) -> Self {
ContractError::UnexpectedError(message.to_string())
}
}
70 changes: 70 additions & 0 deletions tests/error_handling_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use std::error::Error;
use std::fmt::Display;

mod errors {
pub(crate) use crate::src::errors::{ContractError, ContractResult};
}

#[test]
fn test_error_creation() {
// Test initialization error
let init_error = errors::ContractError::initialization("Database connection failed");
assert_eq!(
init_error.to_string(),
"Initialization failed: Database connection failed"
);

// Test token transfer error
let transfer_error = errors::ContractError::token_transfer("Insufficient balance");
assert_eq!(
transfer_error.to_string(),
"Invalid token transfer: Insufficient balance"
);

// Test authorization error
let auth_error = errors::ContractError::authorization("Permission denied");
assert_eq!(
auth_error.to_string(),
"Authorization failed: Permission denied"
);
}

#[test]
fn test_error_conversion() {
// Test validation error
let validation_error = errors::ContractError::validation("Invalid input format");
assert_eq!(
validation_error.to_string(),
"Invalid input: Invalid input format"
);

// Test resource not found error
let not_found_error = errors::ContractError::resource_not_found("User account");
assert_eq!(
not_found_error.to_string(),
"Resource not found: User account"
);
}

#[test]
fn test_error_propagation() {
fn simulate_operation() -> errors::ContractResult<()> {
Err(errors::ContractError::computation("Math overflow"))
}

let result = simulate_operation();
assert!(result.is_err());

if let Err(err) = result {
assert_eq!(err.to_string(), "Computation error: Math overflow");
}
}

#[test]
fn test_unexpected_error() {
let unexpected = errors::ContractError::unexpected("Unhandled system error");
assert_eq!(
unexpected.to_string(),
"Unexpected error occurred: Unhandled system error"
);
}