rialo-api-types 0.1.0

API types for Rialo RPC endpoints
Documentation
// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::fmt;

use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::validation::validate_transaction_data;

/// The configuration for sending a transaction.
/// This is a part of the SendTransaction request.
///
/// Note: Currently only `encoding` and `wait_for_execution` are implemented.
/// The following parameters are parsed but not yet used in the implementation:
/// - max_retries: Will be implemented with retry logic (SUB-47)  
/// - min_context_slot: Will be used for slot-aware transaction submission (SUB-47)
/// - skip_preflight: Will be used to control preflight validation (SUB-47)
///
/// These are kept for compatibility with the Solana CLI and will be implemented
/// as part of the full sendTransaction feature rollout.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Validate)]
pub struct SendTransactionConfig {
    /// Encoding used for the transaction data.
    #[serde(default)]
    pub encoding: TransactionEncoding,

    /// This field exists for compatibility with Solana.
    /// The Rialo implementation ignores it.
    #[validate(range(min = 0, max = 100, message = "Max retries must be between 0 and 100"))]
    #[serde(rename = "maxRetries")]
    pub max_retries: Option<usize>,

    /// This field exists for compatibility with Solana.
    /// The Rialo implementation ignores it.
    #[validate(range(min = 0, message = "Min context slot must be non-negative"))]
    #[serde(rename = "minContextSlot")]
    pub min_context_slot: Option<u64>,

    /// This field exists for compatibility with Solana.
    /// The Rialo implementation ignores it.
    #[serde(rename = "skipPreflight", default)]
    pub skip_preflight: bool,

    /// Whether the RPC node should wait for the transaction execution before replying.
    #[serde(rename = "waitForExecution", default)]
    pub wait_for_execution: bool,
}

impl SendTransactionConfig {
    pub fn new(encoding: TransactionEncoding) -> Self {
        Self {
            encoding,
            max_retries: None,
            min_context_slot: None,
            skip_preflight: false,
            wait_for_execution: false,
        }
    }
}

impl Default for SendTransactionConfig {
    fn default() -> Self {
        Self::new(TransactionEncoding::Base64)
    }
}

/// Request type for sendTransaction RPC handler
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct SendTransactionRequest {
    #[validate(length(min = 1, message = "Transaction cannot be empty"))]
    #[validate(custom(function = validate_transaction_data))]
    pub transaction: String,

    #[validate(nested)]
    pub config: SendTransactionConfig,
}

impl SendTransactionRequest {
    pub fn new(transaction: String, config: SendTransactionConfig) -> Self {
        Self {
            transaction,
            config,
        }
    }
}

/// Supported transaction encodings.
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum TransactionEncoding {
    #[default]
    Base64,
    Base58,
}

impl fmt::Display for TransactionEncoding {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Base64 => f.write_str("base64"),
            Self::Base58 => f.write_str("base58"),
        }
    }
}