multiversx_sdk/gateway/
gateway_chain_simulator_set_state.rs

1use anyhow::anyhow;
2use serde::{Deserialize, Serialize};
3use std::{
4    collections::HashMap,
5    fs::{File, OpenOptions},
6    io::{BufReader, BufWriter},
7    path::Path,
8};
9
10use crate::data::account::Account;
11
12use super::{GatewayRequest, GatewayRequestType, SET_STATE_ENDPOINT};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct SetStateResponse {
16    pub data: serde_json::Value,
17    pub error: String,
18    pub code: String,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, Default)]
22pub struct SetStateAccount {
23    pub address: String,
24    pub nonce: u64,
25    pub balance: String,
26    pub pairs: HashMap<String, String>,
27    pub code: String,
28    #[serde(default)]
29    pub code_hash: String,
30    #[serde(default)]
31    pub root_hash: String,
32    #[serde(default)]
33    pub code_metadata: String,
34    #[serde(default)]
35    pub owner_address: String,
36    #[serde(default)]
37    pub developer_reward: String,
38}
39
40impl From<Account> for SetStateAccount {
41    fn from(value: Account) -> Self {
42        Self {
43            address: value.address.to_bech32_string().unwrap_or_default(),
44            nonce: value.nonce,
45            balance: value.balance.to_string(),
46            pairs: HashMap::new(),
47            code: value.code,
48            code_hash: value.code_hash.unwrap_or_default(),
49            root_hash: value.root_hash.unwrap_or_default(),
50            code_metadata: value.code_metadata.unwrap_or_default(),
51            owner_address: value.owner_address.unwrap_or_default(),
52            developer_reward: value.developer_reward.unwrap_or_default(),
53        }
54    }
55}
56
57impl SetStateAccount {
58    /// Specify the storage key-value pairs to set to the target account.
59    pub fn with_storage(mut self, pairs: HashMap<String, String>) -> Self {
60        self.pairs = pairs;
61        self
62    }
63
64    /// Kept for backwards compatibility.
65    #[deprecated(since = "0.56.0", note = "Use `with_storage` instead.")]
66    pub fn with_keys(self, keys: HashMap<String, String>) -> Self {
67        self.with_storage(keys)
68    }
69
70    pub fn add_to_state_file(self, path: &Path) {
71        let mut accounts = if path.exists() {
72            let file = File::open(path)
73                .unwrap_or_else(|_| panic!("Failed to open state file at path {path:#?}"));
74
75            let reader = BufReader::new(file);
76
77            serde_json::from_reader::<_, Vec<SetStateAccount>>(reader).unwrap_or_default()
78        } else {
79            Vec::new()
80        };
81
82        if let Some(existing_account) = accounts
83            .iter_mut()
84            .find(|account| account.address == self.address)
85        {
86            *existing_account = self;
87        } else {
88            accounts.push(self);
89        }
90
91        let file = OpenOptions::new()
92            .write(true)
93            .create(true)
94            .truncate(true)
95            .open(path)
96            .unwrap_or_else(|_| panic!("Failed to open or create state file at path {path:#?}"));
97
98        let writer = BufWriter::new(file);
99        serde_json::to_writer_pretty(writer, &accounts).unwrap_or_else(|_| {
100            panic!("Failed to write updated state accounts to file at path {path:#?}")
101        });
102    }
103}
104
105/// Sets state for a list of accounts using the chain simulator API.
106pub struct ChainSimulatorSetStateRequest {
107    pub accounts: Vec<SetStateAccount>,
108}
109
110impl ChainSimulatorSetStateRequest {
111    pub fn for_accounts(accounts: Vec<SetStateAccount>) -> Self {
112        Self { accounts }
113    }
114}
115
116impl GatewayRequest for ChainSimulatorSetStateRequest {
117    type Payload = Vec<SetStateAccount>;
118    type DecodedJson = SetStateResponse;
119    type Result = String;
120
121    fn get_payload(&self) -> Option<&Self::Payload> {
122        Some(&self.accounts)
123    }
124
125    fn request_type(&self) -> GatewayRequestType {
126        GatewayRequestType::Post
127    }
128
129    fn get_endpoint(&self) -> String {
130        SET_STATE_ENDPOINT.to_owned()
131    }
132
133    fn process_json(&self, decoded: Self::DecodedJson) -> anyhow::Result<Self::Result> {
134        match decoded.code.as_str() {
135            "successful" => Ok(decoded.code),
136            _ => Err(anyhow!("{}", decoded.error)),
137        }
138    }
139}