pub mod error;
pub mod types;
pub mod rpc;
pub mod fetch;
pub mod reconstruct;
pub mod svm;
pub mod idl;
pub mod trace;
pub mod session;
#[cfg(test)]
pub(crate) mod test_support;
pub use error::ReplayError;
pub use types::{
AccountDelta, AccountMutation, CpiFrame, FetchedTx, FrameAccount, LogDivergence,
ProgramInfo, ProgramLoader, ReconstructedState, Trace, TraceDiff, TxContext, TxResult,
};
pub use rpc::{HeliusClient, HeliusRpcClient};
pub use session::ForkedSession;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature;
use std::str::FromStr;
pub async fn replay<C: HeliusClient>(
signature: &str,
client: &C,
) -> Result<Trace, ReplayError> {
let sig = Signature::from_str(signature)
.map_err(|_| ReplayError::InvalidSignature(signature.to_string()))?;
let mut ctx = fetch::fetch_full_tx_context(client, &sig).await?;
let state = reconstruct::reconstruct_state(client, &ctx).await?;
ctx.pre_account_snapshots = reconstruct::snapshot_pre_state(&state);
let mut runner = svm::SvmRunner::new();
runner.seed(&state)?;
runner.set_clock_for_slot(ctx.slot, ctx.block_time);
let execution = runner.execute(&ctx)?;
let idl_cache = idl::IdlCache::default();
let decoder = idl::AccountDecoder::new(&idl_cache);
let mut trace = trace::build_trace(&ctx, &execution, &decoder).await;
decode_account_deltas(&mut trace, &ctx, &execution, &decoder, client).await;
Ok(trace)
}
pub async fn decode_account_deltas<C: HeliusClient>(
trace: &mut Trace,
ctx: &TxContext,
execution: &svm::ExecutionResult,
decoder: &idl::AccountDecoder<'_>,
client: &C,
) {
for delta in &mut trace.account_deltas {
let Ok(pubkey) = Pubkey::from_str(&delta.pubkey) else {
continue;
};
if let Some(before) = ctx.pre_account_snapshots.get(&pubkey) {
let dec = decoder.decode(&pubkey, before, client).await;
if let idl::DecodedAccount::Decoded { type_name, .. }
| idl::DecodedAccount::Native { type_name, .. } = &dec
{
delta.idl_type_name.get_or_insert_with(|| type_name.clone());
}
delta.decoded_before = serde_json::to_value(&dec).ok();
}
if let Some(after) = execution.accounts_after.get(&pubkey) {
let dec = decoder.decode(&pubkey, after, client).await;
if let idl::DecodedAccount::Decoded { type_name, .. }
| idl::DecodedAccount::Native { type_name, .. } = &dec
{
delta.idl_type_name.get_or_insert_with(|| type_name.clone());
}
delta.decoded_after = serde_json::to_value(&dec).ok();
}
}
}
pub async fn fork<C: HeliusClient>(
signature: &str,
client: &C,
) -> Result<ForkedSession, ReplayError> {
let sig = Signature::from_str(signature)
.map_err(|_| ReplayError::InvalidSignature(signature.to_string()))?;
let mut ctx = fetch::fetch_full_tx_context(client, &sig).await?;
let state = reconstruct::reconstruct_state(client, &ctx).await?;
ctx.pre_account_snapshots = reconstruct::snapshot_pre_state(&state);
ForkedSession::new(ctx, state).await
}