wasm_client_solana/methods/
simulate_transaction.rs1use serde::Deserialize;
2use serde::Deserializer;
3use serde::Serialize;
4use serde::ser::SerializeTuple;
5use solana_transaction::versioned::VersionedTransaction;
6use solana_transaction_error::TransactionError;
7
8use super::Context;
9use crate::deserialize_and_decode;
10use crate::impl_http_method;
11use crate::rpc_config::RpcSimulateTransactionConfig;
12use crate::rpc_config::serialize_and_encode;
13use crate::solana_account_decoder::UiAccount;
14use crate::solana_transaction_status::UiTransactionEncoding;
15use crate::solana_transaction_status::UiTransactionReturnData;
16
17#[derive(Debug, PartialEq, Eq)]
18pub struct SimulateTransactionRequest {
19 pub transaction: VersionedTransaction,
20 pub config: Option<RpcSimulateTransactionConfig>,
21}
22
23impl Serialize for SimulateTransactionRequest {
24 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
25 where
26 S: serde::Serializer,
27 {
28 let encoding = match self.config {
29 Some(ref c) => c.encoding.unwrap_or(UiTransactionEncoding::Base64),
30 None => UiTransactionEncoding::Base64,
31 };
32
33 let serialized_encoded =
34 serialize_and_encode::<VersionedTransaction>(&self.transaction, encoding).unwrap();
35
36 let tuple = if let Some(config) = &self.config {
37 let mut tuple = serializer.serialize_tuple(2)?;
38 tuple.serialize_element(&serialized_encoded)?;
39 tuple.serialize_element(&config)?;
40 tuple
41 } else {
42 let mut tuple = serializer.serialize_tuple(1)?;
43 tuple.serialize_element(&serialized_encoded)?;
44 tuple
45 };
46
47 tuple.end()
48 }
49}
50
51impl<'de> Deserialize<'de> for SimulateTransactionRequest {
52 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53 where
54 D: Deserializer<'de>,
55 {
56 #[derive(Deserialize)]
57 #[serde(rename = "SimulateTransactionRequest")]
58 struct Inner(String, Option<RpcSimulateTransactionConfig>);
59
60 let inner = Inner::deserialize(deserializer)?;
61 let encoding = match inner.1 {
62 Some(ref config) => config.encoding.unwrap_or(UiTransactionEncoding::Base64),
63 None => UiTransactionEncoding::Base64,
64 };
65
66 let transaction =
67 deserialize_and_decode::<VersionedTransaction>(&inner.0, encoding).unwrap();
68
69 Ok(SimulateTransactionRequest {
70 transaction,
71 config: inner.1,
72 })
73 }
74}
75
76impl_http_method!(SimulateTransactionRequest, "simulateTransaction");
77
78impl SimulateTransactionRequest {
79 pub fn new(transaction: VersionedTransaction) -> Self {
80 Self {
81 transaction,
82 config: Some(RpcSimulateTransactionConfig {
83 encoding: Some(UiTransactionEncoding::Base64),
84 replace_recent_blockhash: Some(true),
85 ..Default::default()
86 }),
87 }
88 }
89
90 pub fn new_with_config(
91 transaction: VersionedTransaction,
92 config: RpcSimulateTransactionConfig,
93 ) -> Self {
94 Self {
95 transaction,
96 config: Some(config),
97 }
98 }
99}
100
101#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
102#[serde(rename_all = "camelCase")]
103pub struct SimulateTransactionResponseValue {
104 pub err: Option<TransactionError>,
105 pub logs: Option<Vec<String>>,
106 pub accounts: Option<Vec<Option<UiAccount>>>,
107 pub units_consumed: Option<u64>,
108 pub return_data: Option<UiTransactionReturnData>,
109}
110
111#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
112pub struct SimulateTransactionResponse {
113 pub context: Context,
114 pub value: SimulateTransactionResponseValue,
115}
116
117#[cfg(test)]
118mod tests {
119 use assert2::check;
120 use base64::Engine;
121 use base64::prelude::BASE64_STANDARD;
122 use solana_pubkey::pubkey;
123
124 use super::*;
125 use crate::ClientRequest;
126 use crate::ClientResponse;
127 use crate::methods::HttpMethod;
128 use crate::solana_transaction_status::UiReturnDataEncoding;
129
130 #[test]
131 fn request() {
132 let tx = bincode::deserialize(&BASE64_STANDARD.decode("AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDArczbMia1tLmq7zz4DinMNN0pJ1JtLdqIJPUw3YrGCzYAMHBsgN27lcgB6H2WQvFgyZuJYHa46puOQo9yQ8CVQbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCp20C7Wj2aiuk5TReAXo+VTVg8QTHjs0UjNMMKCvpzZ+ABAgEBARU=").unwrap()).unwrap();
133 let request = ClientRequest::builder()
134 .method(SimulateTransactionRequest::NAME)
135 .id(1)
136 .params(SimulateTransactionRequest::new_with_config(
137 tx,
138 RpcSimulateTransactionConfig {
139 encoding: Some(UiTransactionEncoding::Base64),
140 ..Default::default()
141 },
142 ))
143 .build();
144
145 insta::assert_compact_json_snapshot!(request, @r###"
146 {
147 "jsonrpc": "2.0",
148 "id": 1,
149 "method": "simulateTransaction",
150 "params": [
151 "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDArczbMia1tLmq7zz4DinMNN0pJ1JtLdqIJPUw3YrGCzYAMHBsgN27lcgB6H2WQvFgyZuJYHa46puOQo9yQ8CVQbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCp20C7Wj2aiuk5TReAXo+VTVg8QTHjs0UjNMMKCvpzZ+ABAgEBARU=",
152 {
153 "encoding": "base64",
154 "sigVerify": false
155 }
156 ]
157 }
158 "###);
159 }
160
161 #[test]
162 fn response() {
163 let raw_json = r#"{"jsonrpc":"2.0","result":{"context":{"slot":218},"value":{"err":null,"accounts":null,"logs":["Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri invoke [1]","Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri consumed 2366 of 1400000 compute units","Program return: 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri KgAAAAAAAAA=","Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success"],"returnData":{"data":["Kg==","base64"],"programId":"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"},"unitsConsumed":2366}},"id":1}"#;
164
165 let response: ClientResponse<SimulateTransactionResponse> =
166 serde_json::from_str(raw_json).unwrap();
167
168 check!(response.id == 1);
169 check!(response.jsonrpc == "2.0");
170 check!(response.result.context.slot == 218);
171 check!(
172 response.result.value
173 == SimulateTransactionResponseValue {
174 accounts: None,
175 err: None,
176 logs: Some(vec![
177 "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri invoke [1]"
178 .to_string(),
179 "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri consumed 2366 of \
180 1400000 compute units"
181 .to_string(),
182 "Program return: 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri KgAAAAAAAAA="
183 .to_string(),
184 "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success".to_string()
185 ]),
186 return_data: Some(UiTransactionReturnData {
187 program_id: pubkey!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"),
188 data: ("Kg==".to_string(), UiReturnDataEncoding::Base64)
189 }),
190 units_consumed: Some(2366)
191 }
192 );
193 }
194}