multiversx_sdk/gateway/
gateway_chain_simulator_set_state.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use std::{
    collections::HashMap,
    fs::{File, OpenOptions},
    io::{BufReader, BufWriter},
    path::Path,
};

use crate::data::account::Account;

use super::{GatewayRequest, GatewayRequestType, SET_STATE_ENDPOINT};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetStateResponse {
    pub data: serde_json::Value,
    pub error: String,
    pub code: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SetStateAccount {
    pub address: String,
    pub nonce: u64,
    pub balance: String,
    pub keys: HashMap<String, String>,
    pub code: String,
    #[serde(default)]
    pub code_hash: String,
    #[serde(default)]
    pub root_hash: String,
    #[serde(default)]
    pub code_metadata: String,
    #[serde(default)]
    pub owner_address: String,
    #[serde(default)]
    pub developer_reward: String,
}

impl From<Account> for SetStateAccount {
    fn from(value: Account) -> Self {
        Self {
            address: value.address.to_bech32_string().unwrap_or_default(),
            nonce: value.nonce,
            balance: value.balance.to_string(),
            keys: HashMap::new(),
            code: value.code,
            code_hash: value.code_hash.unwrap_or_default(),
            root_hash: value.root_hash.unwrap_or_default(),
            code_metadata: value.code_metadata.unwrap_or_default(),
            owner_address: value.owner_address.unwrap_or_default(),
            developer_reward: value.developer_reward.unwrap_or_default(),
        }
    }
}

impl SetStateAccount {
    pub fn with_keys(mut self, keys: HashMap<String, String>) -> Self {
        self.keys = keys;

        self
    }

    pub fn add_to_state_file(self, path: &Path) {
        let mut accounts = if path.exists() {
            let file = File::open(path)
                .unwrap_or_else(|_| panic!("Failed to open state file at path {path:#?}"));

            let reader = BufReader::new(file);

            serde_json::from_reader::<_, Vec<SetStateAccount>>(reader).unwrap_or_default()
        } else {
            Vec::new()
        };

        if let Some(existing_account) = accounts
            .iter_mut()
            .find(|account| account.address == self.address)
        {
            *existing_account = self;
        } else {
            accounts.push(self);
        }

        let file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(path)
            .unwrap_or_else(|_| panic!("Failed to open or create state file at path {path:#?}"));

        let writer = BufWriter::new(file);
        serde_json::to_writer_pretty(writer, &accounts).unwrap_or_else(|_| {
            panic!("Failed to write updated state accounts to file at path {path:#?}")
        });
    }
}

/// Sets state for a list of accounts using the chain simulator API.
pub struct ChainSimulatorSetStateRequest {
    pub accounts: Vec<SetStateAccount>,
}

impl ChainSimulatorSetStateRequest {
    pub fn for_accounts(accounts: Vec<SetStateAccount>) -> Self {
        Self { accounts }
    }
}

impl GatewayRequest for ChainSimulatorSetStateRequest {
    type Payload = Vec<SetStateAccount>;
    type DecodedJson = SetStateResponse;
    type Result = String;

    fn get_payload(&self) -> Option<&Self::Payload> {
        Some(&self.accounts)
    }

    fn request_type(&self) -> GatewayRequestType {
        GatewayRequestType::Post
    }

    fn get_endpoint(&self) -> String {
        SET_STATE_ENDPOINT.to_owned()
    }

    fn process_json(&self, decoded: Self::DecodedJson) -> anyhow::Result<Self::Result> {
        match decoded.code.as_str() {
            "successful" => Ok(decoded.code),
            _ => Err(anyhow!("{}", decoded.error)),
        }
    }
}