rialo-s-sdk 0.3.0

Solana SDK
// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Utility functions for creating V0 messages to replace legacy Message usage.
//!
//! This module provides convenient helpers to migrate from legacy Message to V0 Message
//! without requiring address lookup tables when not needed.

use rialo_s_message::{
    v0::{self, Message as V0Message},
    CompileError, ConfigHashPrefix, VersionedMessage,
};
use rialo_s_sdk::{instruction::Instruction, pubkey::Pubkey};

/// Create a V0 message without address lookup tables.
///
/// This is a drop-in replacement for `legacy::Message::new()`.
///
/// # Arguments
/// * `instructions` - The instructions to include in the message
/// * `payer` - The fee payer public key
/// * `valid_from` - The transaction valid from in milliseconds
///
/// # Example
/// ```ignore
/// // Old code:
/// let message = Message::new(&instructions, Some(&payer));
///
/// // New code:
/// let valid_from = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
/// let message = create_v0_message(&instructions, &payer, valid_from)?;
/// ```
pub fn create_v0_message(
    instructions: &[Instruction],
    payer: &Pubkey,
    valid_from: i64,
    config_hash_prefix: ConfigHashPrefix,
    occ: bool,
) -> Result<V0Message, CompileError> {
    // V0 messages without address lookup tables - pass empty array
    v0::Message::try_compile(payer, instructions, valid_from, config_hash_prefix, occ)
}

/// Create a VersionedMessage::V0 without address lookup tables.
///
/// This wraps the V0 message in a VersionedMessage enum for easier use
/// with Transaction APIs that expect VersionedMessage.
///
/// # Arguments
/// * `instructions` - The instructions to include in the message
/// * `payer` - The fee payer public key
/// * `valid_from` - The transaction valid from in milliseconds
pub fn create_versioned_v0_message(
    instructions: &[Instruction],
    payer: &Pubkey,
    valid_from: i64,
    config_hash_prefix: ConfigHashPrefix,
    occ: bool,
) -> Result<VersionedMessage, CompileError> {
    Ok(VersionedMessage::V0(create_v0_message(
        instructions,
        payer,
        valid_from,
        config_hash_prefix,
        occ,
    )?))
}

/// Create a signed VersionedTransaction with V0 message.
///
/// This is a drop-in replacement for `Transaction::new(signers, message, valid_from)`
/// pattern commonly used in tests and utilities.
///
/// # Arguments
/// * `signers` - The keypairs to sign with
/// * `instructions` - The instructions to include
/// * `payer` - The fee payer public key
/// * `valid_from` - The transaction valid from in milliseconds
///
/// # Example
/// ```ignore
/// // Old code:
/// let message = Message::new(&instructions, Some(&payer));
/// let tx = Transaction::new(&[&keypair], message, valid_from);
///
/// // New code:
/// let tx = create_signed_v0_transaction(&[&keypair], &instructions, &payer, valid_from)?;
/// ```
pub fn create_signed_v0_transaction(
    signers: &[&rialo_s_sdk::signature::Keypair],
    instructions: &[Instruction],
    payer: &Pubkey,
    valid_from: i64,
    config_hash_prefix: ConfigHashPrefix,
    occ: bool,
) -> Result<rialo_s_sdk::transaction::VersionedTransaction, Box<dyn std::error::Error>> {
    let message =
        create_versioned_v0_message(instructions, payer, valid_from, config_hash_prefix, occ)?;
    let tx = rialo_s_sdk::transaction::VersionedTransaction::try_new(message, signers)?;
    Ok(tx)
}

/// Create a Transaction (wrapped VersionedTransaction) with V0 message.
///
/// This is the most common pattern - creates a Transaction that wraps a VersionedTransaction::V0.
/// Use this when you need a `Transaction` type (common in tests and node code).
///
/// # Arguments
/// * `signers` - The keypairs to sign with
/// * `instructions` - The instructions to include
/// * `payer` - The fee payer public key
/// * `valid_from` - The transaction valid from in milliseconds
/// * `occ` - Execute the transaction using the OCC scheduler
///
/// # Example
/// ```ignore
/// // Old code:
/// let message = Message::new(&instructions, Some(&payer));
/// let tx = Transaction::new(&[&keypair], message, valid_from);
///
/// // New code:
/// let tx = create_v0_transaction(&[&keypair], &instructions, &payer, valid_from)?;
/// ```
pub fn create_v0_transaction(
    signers: &[&rialo_s_sdk::signature::Keypair],
    instructions: &[Instruction],
    payer: &Pubkey,
    valid_from: i64,
    config_hash_prefix: ConfigHashPrefix,
    occ: bool,
) -> Result<rialo_s_sdk::transaction::VersionedTransaction, Box<dyn std::error::Error>> {
    create_signed_v0_transaction(
        signers,
        instructions,
        payer,
        valid_from,
        config_hash_prefix,
        occ,
    )
}

#[cfg(test)]
mod tests {
    use rialo_s_sdk::system_instruction;

    use super::*;

    #[test]
    fn test_create_v0_message_simple() {
        let from = Pubkey::new_unique();
        let to = Pubkey::new_unique();
        let instruction = system_instruction::transfer(&from, &to, 100);
        let valid_from = 0;
        let config_hash_prefix = ConfigHashPrefix::new(1);

        let message =
            create_v0_message(&[instruction], &from, valid_from, config_hash_prefix, false)
                .unwrap();

        assert_eq!(message.valid_from, valid_from);
        assert_eq!(message.config_hash_prefix, config_hash_prefix);
        assert_eq!(message.instructions.len(), 1);
        assert!(message.account_keys.contains(&from));
        assert!(message.account_keys.contains(&to));
    }

    #[test]
    fn test_create_v0_message_multiple_instructions() {
        let payer = Pubkey::new_unique();
        let to1 = Pubkey::new_unique();
        let to2 = Pubkey::new_unique();
        let instructions = vec![
            system_instruction::transfer(&payer, &to1, 100),
            system_instruction::transfer(&payer, &to2, 200),
        ];
        let valid_from = 0;
        let config_hash_prefix = ConfigHashPrefix::new(1);

        let message =
            create_v0_message(&instructions, &payer, valid_from, config_hash_prefix, false)
                .unwrap();

        assert_eq!(message.instructions.len(), 2);
    }

    #[test]
    fn test_create_versioned_v0_message() {
        let from = Pubkey::new_unique();
        let to = Pubkey::new_unique();
        let instruction = system_instruction::transfer(&from, &to, 100);
        let valid_from = 0;
        let config_hash_prefix = ConfigHashPrefix::new(1);

        let versioned_message = create_versioned_v0_message(
            &[instruction],
            &from,
            valid_from,
            config_hash_prefix,
            false,
        )
        .unwrap();

        match versioned_message {
            VersionedMessage::V0(msg) => {
                assert_eq!(msg.valid_from, valid_from);
                assert_eq!(msg.config_hash_prefix, config_hash_prefix);
                assert_eq!(msg.instructions.len(), 1);
            }
            VersionedMessage::Legacy(_) => panic!("Expected V0 message, got Legacy"),
        }
    }
}