1pub mod error;
14pub mod types;
15pub mod rpc;
16pub mod fetch;
17pub mod reconstruct;
18pub mod svm;
19pub mod idl;
20pub mod trace;
21pub mod session;
22
23#[cfg(test)]
24pub(crate) mod test_support;
25
26pub use error::ReplayError;
27pub use types::{
28 AccountDelta, AccountMutation, CpiFrame, FetchedTx, FrameAccount, LogDivergence,
29 ProgramInfo, ProgramLoader, ReconstructedState, Trace, TraceDiff, TxContext, TxResult,
30};
31pub use rpc::{HeliusClient, HeliusRpcClient};
32pub use session::ForkedSession;
33
34use solana_sdk::pubkey::Pubkey;
35use solana_sdk::signature::Signature;
36use std::str::FromStr;
37
38pub async fn replay<C: HeliusClient>(
44 signature: &str,
45 client: &C,
46) -> Result<Trace, ReplayError> {
47 let sig = Signature::from_str(signature)
48 .map_err(|_| ReplayError::InvalidSignature(signature.to_string()))?;
49
50 let mut ctx = fetch::fetch_full_tx_context(client, &sig).await?;
51 let state = reconstruct::reconstruct_state(client, &ctx).await?;
52 ctx.pre_account_snapshots = reconstruct::snapshot_pre_state(&state);
53
54 let mut runner = svm::SvmRunner::new();
55 runner.seed(&state)?;
56 runner.set_clock_for_slot(ctx.slot, ctx.block_time);
57
58 let execution = runner.execute(&ctx)?;
59
60 let idl_cache = idl::IdlCache::default();
61 let decoder = idl::AccountDecoder::new(&idl_cache);
62
63 let mut trace = trace::build_trace(&ctx, &execution, &decoder).await;
64 decode_account_deltas(&mut trace, &ctx, &execution, &decoder, client).await;
65
66 Ok(trace)
67}
68
69pub async fn decode_account_deltas<C: HeliusClient>(
73 trace: &mut Trace,
74 ctx: &TxContext,
75 execution: &svm::ExecutionResult,
76 decoder: &idl::AccountDecoder<'_>,
77 client: &C,
78) {
79 for delta in &mut trace.account_deltas {
80 let Ok(pubkey) = Pubkey::from_str(&delta.pubkey) else {
81 continue;
82 };
83 if let Some(before) = ctx.pre_account_snapshots.get(&pubkey) {
84 let dec = decoder.decode(&pubkey, before, client).await;
85 if let idl::DecodedAccount::Decoded { type_name, .. }
86 | idl::DecodedAccount::Native { type_name, .. } = &dec
87 {
88 delta.idl_type_name.get_or_insert_with(|| type_name.clone());
89 }
90 delta.decoded_before = serde_json::to_value(&dec).ok();
91 }
92 if let Some(after) = execution.accounts_after.get(&pubkey) {
93 let dec = decoder.decode(&pubkey, after, client).await;
94 if let idl::DecodedAccount::Decoded { type_name, .. }
95 | idl::DecodedAccount::Native { type_name, .. } = &dec
96 {
97 delta.idl_type_name.get_or_insert_with(|| type_name.clone());
98 }
99 delta.decoded_after = serde_json::to_value(&dec).ok();
100 }
101 }
102}
103
104pub async fn fork<C: HeliusClient>(
111 signature: &str,
112 client: &C,
113) -> Result<ForkedSession, ReplayError> {
114 let sig = Signature::from_str(signature)
115 .map_err(|_| ReplayError::InvalidSignature(signature.to_string()))?;
116
117 let mut ctx = fetch::fetch_full_tx_context(client, &sig).await?;
118 let state = reconstruct::reconstruct_state(client, &ctx).await?;
119 ctx.pre_account_snapshots = reconstruct::snapshot_pre_state(&state);
120
121 ForkedSession::new(ctx, state).await
122}