rialo-cdk 0.2.0-alpha.0

Rialo CDK - A comprehensive toolkit for building with the Rialo blockchain
Documentation
// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Airdrop utility functions for the Rialo CDK.
//!
//! This module provides helper functions for requesting airdrops from the
//! faucet service. These utilities are only available on non-WASM targets
//! as they depend on transaction confirmation utilities that require tokio.

#[cfg(not(target_arch = "wasm32"))]
use crate::rpc::transaction_utils::wait_for_confirmation;
use crate::{
    error::Result,
    rpc::{
        traits::RpcClient,
        types::{Pubkey, Signature},
    },
};

/// Requests an airdrop and waits for confirmation.
///
/// This is a convenience function that combines requesting an airdrop from
/// the faucet with waiting for the transaction to be confirmed on-chain.
///
/// # Arguments
///
/// * `client` - The RPC client to use for the airdrop request and confirmation.
/// * `pubkey` - The public key (address) of the account to receive the airdrop.
/// * `amount` - The amount of tokens to airdrop in the smallest denomination (kelvins).
/// * `timeout_ms` - Optional timeout in milliseconds to wait for confirmation.
///   Defaults to 5000ms if not specified.
///
/// # Returns
///
/// * `Ok(Signature)` - The transaction signature of the confirmed airdrop.
/// * `Err(...)` - An error if the airdrop request fails or confirmation times out.
///
/// # Availability
///
/// This function is only available on non-WASM targets as it requires
/// async timer functionality for confirmation polling.
///
/// # Examples
///
/// ```no_run
/// # use rialo_cdk::{RpcClient, rpc::HttpRpcClient};
/// # use rialo_cdk::rpc::airdrop::request_airdrop_with_confirmation;
/// # use rialo_cdk::rpc::types::Pubkey;
/// # use std::str::FromStr;
/// # async fn example() -> rialo_cdk::Result<()> {
/// # let client = HttpRpcClient::new("http://localhost:8899".to_string());
/// let pubkey = Pubkey::from_str("11111111111111111111111111111111")?;
///
/// // Request 1 RLO (1_000_000_000 kelvins) with default timeout
/// let signature = request_airdrop_with_confirmation(
///     &client,
///     &pubkey,
///     1_000_000_000,
///     None
/// ).await?;
///
/// println!("Airdrop confirmed: {}", signature);
/// # Ok(())
/// # }
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub async fn request_airdrop_with_confirmation(
    client: &impl RpcClient,
    pubkey: &Pubkey,
    amount: u64,
    timeout_ms: Option<u32>,
) -> Result<Signature> {
    // Request the airdrop through the RPC client
    let signature = client.request_airdrop(pubkey, amount).await?;

    // Wait for the transaction to confirm
    wait_for_confirmation(client, &signature, timeout_ms).await?;

    Ok(signature)
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use super::*;
    use crate::rpc::{mock_client::MockRpcClient, types::SignatureStatus};

    #[cfg(not(target_arch = "wasm32"))]
    #[tokio::test]
    async fn test_airdrop_with_confirmation_success() {
        let expected_sig_str = "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW";
        let expected_sig = Signature::from_str(expected_sig_str).unwrap();

        let mock_rpc = MockRpcClient::new()
            .with_signature(expected_sig)
            .with_signature_statuses(vec![Some(SignatureStatus {
                slot: 100,
                err: None,
                executed: true,
            })]);

        let pubkey = Pubkey::new_unique();
        let result =
            request_airdrop_with_confirmation(&mock_rpc, &pubkey, 1_000_000_000, None).await;

        assert!(result.is_ok(), "Airdrop should succeed");
        let signature = result.unwrap();
        assert_eq!(signature.to_string(), expected_sig_str);
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[tokio::test]
    async fn test_airdrop_with_confirmation_timeout() {
        let expected_sig = Signature::from_str(
            "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
        )
        .unwrap();

        let mock_rpc = MockRpcClient::new()
            .with_signature(expected_sig)
            .with_signature_statuses(vec![Some(SignatureStatus {
                slot: 100,
                err: None,
                executed: false, // Never executes
            })]);

        let pubkey = Pubkey::new_unique();
        // Use short timeout for test
        let result =
            request_airdrop_with_confirmation(&mock_rpc, &pubkey, 1_000_000_000, Some(100)).await;

        assert!(result.is_err(), "Should timeout");
        assert!(
            matches!(
                result.unwrap_err(),
                crate::error::RialoError::TransactionTimeout
            ),
            "Should be TransactionTimeout error"
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[tokio::test]
    async fn test_airdrop_with_custom_timeout() {
        let expected_sig = Signature::from_str(
            "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
        )
        .unwrap();

        let mock_rpc = MockRpcClient::new()
            .with_signature(expected_sig)
            .with_signature_statuses(vec![Some(SignatureStatus {
                slot: 100,
                err: None,
                executed: true,
            })]);

        let pubkey = Pubkey::new_unique();
        let result =
            request_airdrop_with_confirmation(&mock_rpc, &pubkey, 1_000_000_000, Some(10000)).await;

        assert!(result.is_ok(), "Should succeed with custom timeout");
    }
}