drt_sc_snippets/
network_response.rs1use drt_sc_scenario::{
2 imports::{Address, DCDTSystemSCAddress},
3 drt_chain_vm::crypto_functions::keccak256,
4 scenario_model::{TxResponse, TxResponseStatus},
5};
6use drt_sdk::{
7 data::transaction::{ApiSmartContractResult, Events, TransactionOnNetwork},
8 utils::base64_decode,
9};
10
11const SC_DEPLOY_PROCESSING_TYPE: &str = "SCDeployment";
12const LOG_IDENTIFIER_SIGNAL_ERROR: &str = "signalError";
13
14pub fn parse_tx_response(tx: TransactionOnNetwork) -> TxResponse {
16 let tx_error = process_signal_error(&tx);
17 if !tx_error.is_success() {
18 return TxResponse {
19 tx_error,
20 ..Default::default()
21 };
22 }
23
24 process_success(&tx)
25}
26
27fn process_signal_error(tx: &TransactionOnNetwork) -> TxResponseStatus {
28 if let Some(event) = find_log(tx, LOG_IDENTIFIER_SIGNAL_ERROR) {
29 let topics = event.topics.as_ref();
30 if let Some(error) = process_topics_error(topics) {
31 return TxResponseStatus::signal_error(&error);
32 }
33
34 let error_raw = base64_decode(topics.unwrap().get(1).unwrap());
35 let error = String::from_utf8(error_raw).unwrap();
36 return TxResponseStatus::signal_error(&error);
37 }
38
39 TxResponseStatus::default()
40}
41
42fn process_success(tx: &TransactionOnNetwork) -> TxResponse {
43 TxResponse {
44 out: process_out(tx),
45 new_deployed_address: process_new_deployed_address(tx),
46 new_issued_token_identifier: process_new_issued_token_identifier(tx),
47 ..Default::default()
48 }
49}
50
51fn process_out(tx: &TransactionOnNetwork) -> Vec<Vec<u8>> {
52 let out_scr = tx.smart_contract_results.iter().find(is_out_scr);
53
54 if let Some(out_scr) = out_scr {
55 decode_scr_data_or_panic(&out_scr.data)
56 } else {
57 process_out_from_log(tx).unwrap_or_default()
58 }
59}
60
61fn process_out_from_log(tx: &TransactionOnNetwork) -> Option<Vec<Vec<u8>>> {
62 if let Some(logs) = &tx.logs {
63 logs.events.iter().rev().find_map(|event| {
64 if event.identifier == "writeLog" {
65 if let Some(data) = &event.data {
66 let decoded_data = String::from_utf8(base64_decode(data)).unwrap();
67
68 if decoded_data.starts_with('@') {
69 let out = decode_scr_data_or_panic(decoded_data.as_str());
70 return Some(out);
71 }
72 }
73 }
74
75 None
76 })
77 } else {
78 None
79 }
80}
81
82fn process_new_deployed_address(tx: &TransactionOnNetwork) -> Option<Address> {
83 if tx.processing_type_on_destination != SC_DEPLOY_PROCESSING_TYPE {
84 return None;
85 }
86
87 let sender_address_bytes = tx.sender.to_bytes();
88 let sender_nonce_bytes = tx.nonce.to_le_bytes();
89 let mut bytes_to_hash: Vec<u8> = Vec::new();
90 bytes_to_hash.extend_from_slice(&sender_address_bytes);
91 bytes_to_hash.extend_from_slice(&sender_nonce_bytes);
92
93 let address_keccak = keccak256(&bytes_to_hash);
94
95 let mut address = [0u8; 32];
96
97 address[0..8].copy_from_slice(&[0u8; 8]);
98 address[8..10].copy_from_slice(&[5, 0]);
99 address[10..30].copy_from_slice(&address_keccak[10..30]);
100 address[30..32].copy_from_slice(&sender_address_bytes[30..32]);
101
102 Some(Address::from(address))
103}
104
105fn process_new_issued_token_identifier(tx: &TransactionOnNetwork) -> Option<String> {
106 for scr in tx.smart_contract_results.iter() {
107 if scr.sender.to_bech32_string().unwrap() != DCDTSystemSCAddress.to_bech32_string() {
108 continue;
109 }
110
111 let Some(prev_tx) = tx
112 .smart_contract_results
113 .iter()
114 .find(|e| e.hash == scr.prev_tx_hash)
115 else {
116 continue;
117 };
118
119 let is_issue_fungible = prev_tx.data.starts_with("issue@");
120 let is_issue_semi_fungible = prev_tx.data.starts_with("issueSemiFungible@");
121 let is_issue_non_fungible = prev_tx.data.starts_with("issueNonFungible@");
122 let is_register_meta_dcdt = prev_tx.data.starts_with("registerMetaDCDT@");
123 let is_register_and_set_all_roles_dcdt =
124 prev_tx.data.starts_with("registerAndSetAllRoles@");
125
126 if !is_issue_fungible
127 && !is_issue_semi_fungible
128 && !is_issue_non_fungible
129 && !is_register_meta_dcdt
130 && !is_register_and_set_all_roles_dcdt
131 {
132 continue;
133 }
134
135 if scr.data.starts_with("DCDTTransfer@") {
136 let encoded_tid = scr.data.split('@').nth(1);
137 return Some(String::from_utf8(hex::decode(encoded_tid?).unwrap()).unwrap());
138 } else if scr.data.starts_with("@00@") {
139 let encoded_tid = scr.data.split('@').nth(2);
140 return Some(String::from_utf8(hex::decode(encoded_tid?).unwrap()).unwrap());
141 }
142 }
143
144 None
145}
146
147fn find_log<'a>(tx: &'a TransactionOnNetwork, log_identifier: &str) -> Option<&'a Events> {
148 if let Some(logs) = &tx.logs {
149 logs.events
150 .iter()
151 .find(|event| event.identifier == log_identifier)
152 } else {
153 None
154 }
155}
156
157pub fn process_topics_error(topics: Option<&Vec<String>>) -> Option<String> {
159 if topics.is_none() {
160 return Some("missing topics".to_string());
161 }
162
163 let topics = topics.unwrap();
164 if topics.len() != 2 {
165 Some(format!(
166 "expected to have 2 topics, found {} instead",
167 topics.len()
168 ))
169 } else {
170 None
171 }
172}
173
174pub fn decode_scr_data_or_panic(data: &str) -> Vec<Vec<u8>> {
176 let mut split = data.split('@');
177 let _ = split.next().expect("SCR data should start with '@'");
178 let result_code = split.next().expect("missing result code");
179 assert_eq!(result_code, "6f6b", "result code is not 'ok'");
180
181 split
182 .map(|encoded_arg| hex::decode(encoded_arg).expect("error hex-decoding result"))
183 .collect()
184}
185
186pub fn is_out_scr(scr: &&ApiSmartContractResult) -> bool {
188 scr.nonce != 0 && scr.data.starts_with('@')
189}