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.bech32,
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    /// Creates a SetStateAccount from an address
65    pub fn from_address(address: String) -> Self {
66        Self {
67            address,
68            ..Default::default()
69        }
70    }
71
72    /// Kept for backwards compatibility.
73    #[deprecated(since = "0.56.0", note = "Use `with_storage` instead.")]
74    pub fn with_keys(self, keys: HashMap<String, String>) -> Self {
75        self.with_storage(keys)
76    }
77
78    pub fn add_to_state_file(self, path: &Path) {
79        let mut accounts = if path.exists() {
80            let file = File::open(path)
81                .unwrap_or_else(|_| panic!("Failed to open state file at path {path:#?}"));
82
83            let reader = BufReader::new(file);
84
85            serde_json::from_reader::<_, Vec<SetStateAccount>>(reader).unwrap_or_default()
86        } else {
87            Vec::new()
88        };
89
90        if let Some(existing_account) = accounts
91            .iter_mut()
92            .find(|account| account.address == self.address)
93        {
94            *existing_account = self;
95        } else {
96            accounts.push(self);
97        }
98
99        let file = OpenOptions::new()
100            .write(true)
101            .create(true)
102            .truncate(true)
103            .open(path)
104            .unwrap_or_else(|_| panic!("Failed to open or create state file at path {path:#?}"));
105
106        let writer = BufWriter::new(file);
107        serde_json::to_writer_pretty(writer, &accounts).unwrap_or_else(|_| {
108            panic!("Failed to write updated state accounts to file at path {path:#?}")
109        });
110    }
111}
112
113/// Sets state for a list of accounts using the chain simulator API.
114pub struct ChainSimulatorSetStateRequest {
115    pub accounts: Vec<SetStateAccount>,
116}
117
118impl ChainSimulatorSetStateRequest {
119    pub fn for_accounts(accounts: Vec<SetStateAccount>) -> Self {
120        Self { accounts }
121    }
122}
123
124impl GatewayRequest for ChainSimulatorSetStateRequest {
125    type Payload = Vec<SetStateAccount>;
126    type DecodedJson = SetStateResponse;
127    type Result = String;
128
129    fn get_payload(&self) -> Option<&Self::Payload> {
130        Some(&self.accounts)
131    }
132
133    fn request_type(&self) -> GatewayRequestType {
134        GatewayRequestType::Post
135    }
136
137    fn get_endpoint(&self) -> String {
138        SET_STATE_ENDPOINT.to_owned()
139    }
140
141    fn process_json(&self, decoded: Self::DecodedJson) -> anyhow::Result<Self::Result> {
142        match decoded.code.as_str() {
143            "successful" => Ok(decoded.code),
144            _ => Err(anyhow!("{}", decoded.error)),
145        }
146    }
147}