neo-types 0.14.0

Neo N3 Core Types for Smart Contract Development
Documentation
// Copyright (c) 2025-2026 R3E Network
// Licensed under the MIT License

use std::fmt;
use std::string::String;

/// Neo N3 Error type
///
/// Represents all possible error conditions that can occur during
/// Neo N3 smart contract execution.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NeoError {
    /// Operation is not valid in the current context
    InvalidOperation,
    /// Argument has an invalid value or type
    InvalidArgument,
    /// Type mismatch encountered during execution
    InvalidType,
    /// Index or offset is out of valid bounds
    OutOfBounds,
    /// Attempted division by zero
    DivisionByZero,
    /// Arithmetic overflow occurred
    Overflow,
    /// Arithmetic underflow occurred
    Underflow,
    /// Dereferenced a null or invalid reference
    NullReference,
    /// Internal state is invalid or corrupted
    InvalidState,
    /// Application-specific error with custom message
    Custom(String),
    /// L6: a wasm32 cross-call syscall (System.Contract.Call,
    /// System.Runtime.LoadScript, System.Contract.CallNative) was
    /// invoked, but the wasm32 cross-call executor is not yet
    /// implemented. Replaces the v0.8.0 B4 panic-loud sites with
    /// a structured error.
    Wasm32CrossCallUnavailable { syscall: &'static str },
}

impl fmt::Display for NeoError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            NeoError::InvalidOperation => write!(f, "Invalid operation: the requested operation cannot be performed in the current context"),
            NeoError::InvalidArgument => write!(f, "Invalid argument: one or more arguments have invalid values or types"),
            NeoError::InvalidType => write!(f, "Invalid type: type mismatch encountered during execution"),
            NeoError::OutOfBounds => write!(f, "Out of bounds: index or offset exceeds valid range"),
            NeoError::DivisionByZero => write!(f, "Division by zero: cannot divide by zero"),
            NeoError::Overflow => write!(f, "Overflow: arithmetic operation resulted in overflow"),
            NeoError::Underflow => write!(f, "Underflow: arithmetic operation resulted in underflow"),
            NeoError::NullReference => write!(f, "Null reference: attempted to access a null or invalid reference"),
            NeoError::InvalidState => write!(f, "Invalid state: internal state is invalid or corrupted"),
            NeoError::Custom(msg) => write!(f, "{}", msg),
            NeoError::Wasm32CrossCallUnavailable { syscall } => {
                write!(f, "wasm32 cross-call unavailable: {syscall}")
            }
        }
    }
}

impl NeoError {
    /// Creates a new custom error with the given message.
    ///
    /// # Examples
    ///
    /// ```
    /// use neo_types::NeoError;
    ///
    /// let err = NeoError::new("Custom error message");
    /// ```
    pub fn new(message: &str) -> Self {
        NeoError::Custom(message.to_string())
    }

    /// Returns a stable status code for this error variant.
    ///
    /// These codes are used by generated export wrappers to avoid silently
    /// treating failures as successful `0` values.
    pub fn status_code(&self) -> i64 {
        match self {
            NeoError::InvalidOperation => 1,
            NeoError::InvalidArgument => 2,
            NeoError::InvalidType => 3,
            NeoError::OutOfBounds => 4,
            NeoError::DivisionByZero => 5,
            NeoError::Overflow => 6,
            NeoError::Underflow => 7,
            NeoError::NullReference => 8,
            NeoError::InvalidState => 9,
            NeoError::Custom(_) => 10,
            NeoError::Wasm32CrossCallUnavailable { .. } => 11,
        }
    }

    /// Returns the error message if this is a custom error, otherwise returns a generic description.
    pub fn message(&self) -> &str {
        match self {
            NeoError::Custom(msg) => msg,
            NeoError::Wasm32CrossCallUnavailable { syscall } => {
                // `message()` returns `&str`, so it cannot allocate the
                // "<prefix>: <syscall>" form that `Display` builds. Since the
                // syscall is a `&'static str` from a closed set, return a
                // matching static literal per known syscall.
                match *syscall {
                    "System.Contract.Call" => "wasm32 cross-call unavailable: System.Contract.Call",
                    "System.Runtime.LoadScript" => {
                        "wasm32 cross-call unavailable: System.Runtime.LoadScript"
                    }
                    "System.Contract.CallNative" => {
                        "wasm32 cross-call unavailable: System.Contract.CallNative"
                    }
                    _ => "wasm32 cross-call unavailable",
                }
            }
            _ => self.as_str(),
        }
    }

    /// Returns a static string description of the error variant.
    pub fn as_str(&self) -> &'static str {
        match self {
            NeoError::InvalidOperation => "InvalidOperation",
            NeoError::InvalidArgument => "InvalidArgument",
            NeoError::InvalidType => "InvalidType",
            NeoError::OutOfBounds => "OutOfBounds",
            NeoError::DivisionByZero => "DivisionByZero",
            NeoError::Overflow => "Overflow",
            NeoError::Underflow => "Underflow",
            NeoError::NullReference => "NullReference",
            NeoError::InvalidState => "InvalidState",
            NeoError::Custom(_) => "Custom",
            NeoError::Wasm32CrossCallUnavailable { .. } => "Wasm32CrossCallUnavailable",
        }
    }
}

impl std::error::Error for NeoError {}

// D16: ergonomic `From` conversions so `?` works across common stdlib error
// types encountered when parsing integers/bytes in contract code.
impl From<std::num::TryFromIntError> for NeoError {
    fn from(_: std::num::TryFromIntError) -> Self {
        NeoError::Overflow
    }
}

impl From<std::num::ParseIntError> for NeoError {
    fn from(_: std::num::ParseIntError) -> Self {
        NeoError::InvalidArgument
    }
}

/// Neo N3 Result type
pub type NeoResult<T> = Result<T, NeoError>;