Skip to content

Commit 1410be3

Browse files
Merge pull request #2124 from multiversx/interactor-multi-transfer-fix
interactor multi-transfer result test & fix
2 parents 21e9f31 + dbb0ffd commit 1410be3

File tree

6 files changed

+268
-19
lines changed

6 files changed

+268
-19
lines changed

contracts/feature-tests/payable-features/interactor/config.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
# gateway_uri = 'http://localhost:8085'
44

55
chain_type = 'real'
6-
gateway_uri = 'https://devnet-gateway.multiversx.com'
7-
6+
gateway_uri = 'https://testnet-gateway.multiversx.com'

contracts/feature-tests/payable-features/interactor/tests/payable_interactor_cs_test.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use serial_test::serial;
33

44
#[tokio::test]
55
#[serial]
6-
#[ignore = "TODO: find issue in the CI"]
7-
// #[cfg_attr(not(feature = "chain-simulator-tests"), ignore)]
6+
#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)]
87
async fn payable_interactor_test() {
98
let mut payable_interact = PayableInteract::new(Config::chain_simulator_config()).await;
109

framework/meta/src/cmd/install/install_scenario_go.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ impl ScenarioGoInstaller {
9999

100100
let tag_name = parsed
101101
.get("tag_name")
102-
.expect("tag name not found")
102+
.unwrap_or_else(|| panic!("tag name not found in response: {raw_json}"))
103103
.as_str()
104104
.expect("malformed json");
105105

framework/snippets/src/network_response.rs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::sdk::{
2-
data::transaction::{ApiSmartContractResult, Events, TransactionOnNetwork},
2+
data::transaction::{ApiLogs, ApiSmartContractResult, Events, TransactionOnNetwork},
33
utils::base64_decode,
44
};
55
use multiversx_sc_scenario::{
@@ -57,13 +57,20 @@ fn process_tx_hash(tx: &TransactionOnNetwork) -> Option<H256> {
5757
}
5858

5959
fn process_out(tx: &TransactionOnNetwork) -> Vec<Vec<u8>> {
60+
let out_multi_transfer = tx.smart_contract_results.iter().find(is_multi_transfer);
6061
let out_scr = tx.smart_contract_results.iter().find(is_out_scr);
6162

62-
if let Some(out_scr) = out_scr {
63-
decode_scr_data_or_panic(&out_scr.data)
64-
} else {
65-
process_out_from_log(tx).unwrap_or_default()
63+
if let Some(out_multi_transfer) = out_multi_transfer {
64+
log::trace!("Parsing result from multi transfer: {out_multi_transfer:?}");
65+
return decode_multi_transfer_data_or_panic(out_multi_transfer.logs.clone())
66+
.unwrap_or_default();
67+
} else if let Some(out_scr) = out_scr {
68+
log::trace!("Parsing result from scr: {out_scr:?}");
69+
return decode_scr_data_or_panic(&out_scr.data);
6670
}
71+
72+
log::trace!("Parsing result from logs");
73+
process_out_from_log(tx).unwrap_or_default()
6774
}
6875

6976
fn process_logs(tx: &TransactionOnNetwork) -> Vec<Log> {
@@ -105,15 +112,7 @@ fn process_out_from_log(tx: &TransactionOnNetwork) -> Option<Vec<Vec<u8>>> {
105112
if let Some(logs) = &tx.logs {
106113
logs.events.iter().rev().find_map(|event| {
107114
if event.identifier == "writeLog" {
108-
let mut out = Vec::new();
109-
event.data.for_each(|data_member| {
110-
let decoded_data = String::from_utf8(base64_decode(data_member)).unwrap();
111-
112-
if decoded_data.starts_with('@') {
113-
let out_content = decode_scr_data_or_panic(decoded_data.as_str());
114-
out.extend(out_content);
115-
}
116-
});
115+
let out = extract_write_log_data(event);
117116
return Some(out);
118117
}
119118

@@ -238,7 +237,42 @@ pub fn decode_scr_data_or_panic(data: &str) -> Vec<Vec<u8>> {
238237
.collect()
239238
}
240239

240+
/// Decodes the data of a multi transfer result.
241+
pub fn decode_multi_transfer_data_or_panic(logs: Option<ApiLogs>) -> Option<Vec<Vec<u8>>> {
242+
let logs = logs.expect("missing logs");
243+
244+
if let Some(event) = logs
245+
.events
246+
.iter()
247+
.find(|event| event.identifier == "writeLog")
248+
{
249+
let out = extract_write_log_data(event);
250+
return Some(out);
251+
}
252+
253+
None
254+
}
255+
256+
fn extract_write_log_data(event: &Events) -> Vec<Vec<u8>> {
257+
let mut out = Vec::new();
258+
event.data.for_each(|data_member| {
259+
let decoded_data = String::from_utf8(base64_decode(data_member)).unwrap();
260+
261+
if decoded_data.starts_with('@') {
262+
let out_content = decode_scr_data_or_panic(decoded_data.as_str());
263+
out.extend(out_content);
264+
}
265+
});
266+
267+
out
268+
}
269+
241270
/// Checks if the given smart contract result is an out smart contract result.
242271
pub fn is_out_scr(scr: &&ApiSmartContractResult) -> bool {
243272
scr.nonce != 0 && scr.data.starts_with('@')
244273
}
274+
275+
/// Checks if the given smart contract result is a multi transfer smart contract result.
276+
pub fn is_multi_transfer(scr: &&ApiSmartContractResult) -> bool {
277+
scr.data.starts_with("MultiESDTNFTTransfer@")
278+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
use multiversx_sc_scenario::imports::ReturnCode;
2+
use multiversx_sc_snippets::network_response;
3+
use multiversx_sdk::data::transaction::{TransactionInfo, TransactionOnNetwork};
4+
5+
#[test]
6+
fn test_tx_payable_features_egld() {
7+
let data = r#"
8+
{
9+
"data": {
10+
"transaction": {
11+
"type": "normal",
12+
"processingTypeOnSource": "BuiltInFunctionCall",
13+
"processingTypeOnDestination": "SCInvoking",
14+
"hash": "29c295934b5419dc10756c6ddf45468f4de3ce4a1f7ba236aeee0e7275784a89",
15+
"nonce": 5,
16+
"round": 1297249,
17+
"epoch": 1080,
18+
"value": "0",
19+
"receiver": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
20+
"sender": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
21+
"gasPrice": 1000000000,
22+
"gasLimit": 6000000,
23+
"gasUsed": 6000000,
24+
"data": "TXVsdGlFU0RUTkZUVHJhbnNmZXJAMDAwMDAwMDAwMDAwMDAwMDA1MDA0NjY1ZWRiYWNhNmI3NjMxMWJkYjA3ZWNjMjlhMDNiYzBlMDk5YzlkMGZkNkAwMkA0NTQ3NGM0NDJkMzAzMDMwMzAzMDMwQEAyNzEwQDUyNGY1MzQ1NTQ1NDQxMmQzMTM1MzkzNzYyMzhAQDAxQDcwNjE3OTYxNjI2YzY1NWY2MTZjNmM1Zjc0NzI2MTZlNzM2NjY1NzI3Mw==",
25+
"signature": "2facfcf63bab6d48fa4d0f9fbe710fddccd43e6dd1907264f3ee824a61fa91a06e8cd40e1e0eb71d3703241c4c4cbb645ba4de8bfcfb9ab38bed8fccf33b6b0b",
26+
"sourceShard": 1,
27+
"destinationShard": 1,
28+
"blockNonce": 1295602,
29+
"blockHash": "7097493c311ec6733c87cff78c12abbea294e57904ba40c93d1f95a2017646b4",
30+
"notarizedAtSourceInMetaNonce": 1296516,
31+
"NotarizedAtSourceInMetaHash": "cebb239cb97d17e83ca4a0eb114e123bf3deed1e5726c090382590a49d32342a",
32+
"notarizedAtDestinationInMetaNonce": 1296516,
33+
"notarizedAtDestinationInMetaHash": "cebb239cb97d17e83ca4a0eb114e123bf3deed1e5726c090382590a49d32342a",
34+
"miniblockType": "TxBlock",
35+
"miniblockHash": "64d77ede30339030f8b4211528ac12380e6765af607414cc41d86db67cf210a2",
36+
"hyperblockNonce": 1296516,
37+
"hyperblockHash": "cebb239cb97d17e83ca4a0eb114e123bf3deed1e5726c090382590a49d32342a",
38+
"timestamp": 1753198494,
39+
"timestampMs": 1753198494000,
40+
"smartContractResults": [
41+
{
42+
"hash": "df1842706d9014a4000b0a18c8d8a19a7d468e2d969564e1665d9bd6947b8630",
43+
"nonce": 0,
44+
"value": 0,
45+
"receiver": "erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
46+
"sender": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
47+
"data": "MultiESDTNFTTransfer@02@45474c442d303030303030@00@2710@524f53455454412d313539376238@00@01@70617961626c655f616c6c5f7472616e7366657273",
48+
"prevTxHash": "29c295934b5419dc10756c6ddf45468f4de3ce4a1f7ba236aeee0e7275784a89",
49+
"originalTxHash": "29c295934b5419dc10756c6ddf45468f4de3ce4a1f7ba236aeee0e7275784a89",
50+
"gasLimit": 5260500,
51+
"gasPrice": 1000000000,
52+
"callType": 0,
53+
"originalSender": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
54+
"logs": {
55+
"address": "erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
56+
"events": [
57+
{
58+
"address": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
59+
"identifier": "MultiESDTNFTTransfer",
60+
"topics": [
61+
"RUdMRC0wMDAwMDA=",
62+
"",
63+
"JxA=",
64+
"Uk9TRVRUQS0xNTk3Yjg=",
65+
"",
66+
"AQ==",
67+
"AAAAAAAAAAAFAEZl7brKa3YxG9sH7MKaA7wOCZydD9Y="
68+
],
69+
"data": null,
70+
"additionalData": [
71+
"",
72+
"TXVsdGlFU0RUTkZUVHJhbnNmZXI=",
73+
"Ag==",
74+
"RUdMRC0wMDAwMDA=",
75+
"AA==",
76+
"JxA=",
77+
"Uk9TRVRUQS0xNTk3Yjg=",
78+
"AA==",
79+
"AQ==",
80+
"cGF5YWJsZV9hbGxfdHJhbnNmZXJz"
81+
]
82+
},
83+
{
84+
"address": "erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
85+
"identifier": "writeLog",
86+
"topics": [
87+
"iZRRs2GoPonXO0CW08kMIJsnh0ycfLAbsIsLtNwVaT0=",
88+
"QHRvbyBtdWNoIGdhcyBwcm92aWRlZCBmb3IgcHJvY2Vzc2luZzogZ2FzIHByb3ZpZGVkID0gNTI2MDUwMCwgZ2FzIHVzZWQgPSAxNjU1NDI1"
89+
],
90+
"data": "QDZmNmJAMDAwMDAwMDQ0NTQ3NGM0NDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjI3MTAwMDAwMDAwZTUyNGY1MzQ1NTQ1NDQxMmQzMTM1MzkzNzYyMzgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMQ==",
91+
"additionalData": [
92+
"QDZmNmJAMDAwMDAwMDQ0NTQ3NGM0NDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjI3MTAwMDAwMDAwZTUyNGY1MzQ1NTQ1NDQxMmQzMTM1MzkzNzYyMzgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMQ=="
93+
]
94+
},
95+
{
96+
"address": "erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
97+
"identifier": "completedTxEvent",
98+
"topics": [
99+
"KcKVk0tUGdwQdWxt30VGj03jzkofe6I2ru4OcnV4Sok="
100+
],
101+
"data": null,
102+
"additionalData": null
103+
}
104+
]
105+
},
106+
"tokens": [
107+
"EGLD-000000",
108+
"ROSETTA-1597b8"
109+
],
110+
"esdtValues": [
111+
"10000",
112+
"1"
113+
],
114+
"receivers": [
115+
"erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
116+
"erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq"
117+
],
118+
"receiversShardIDs": [
119+
2,
120+
2
121+
],
122+
"operation": "MultiESDTNFTTransfer",
123+
"function": "payable_all_transfers"
124+
},
125+
{
126+
"hash": "2b6a15aef02cbe2329e9a3fc590493bce3ad5fbfa75e96e2086b90aec63f1e73",
127+
"nonce": 0,
128+
"value": 0,
129+
"receiver": "erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
130+
"sender": "erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
131+
"data": "payable_all_transfers",
132+
"prevTxHash": "df1842706d9014a4000b0a18c8d8a19a7d468e2d969564e1665d9bd6947b8630",
133+
"originalTxHash": "29c295934b5419dc10756c6ddf45468f4de3ce4a1f7ba236aeee0e7275784a89",
134+
"gasLimit": 5260500,
135+
"gasPrice": 1000000000,
136+
"callType": 0,
137+
"originalSender": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
138+
"operation": "transfer",
139+
"function": "payable_all_transfers"
140+
}
141+
],
142+
"logs": {
143+
"address": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
144+
"events": [
145+
{
146+
"address": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx",
147+
"identifier": "MultiESDTNFTTransfer",
148+
"topics": [
149+
"RUdMRC0wMDAwMDA=",
150+
"",
151+
"JxA=",
152+
"Uk9TRVRUQS0xNTk3Yjg=",
153+
"",
154+
"AQ==",
155+
"AAAAAAAAAAAFAEZl7brKa3YxG9sH7MKaA7wOCZydD9Y="
156+
],
157+
"data": null,
158+
"additionalData": [
159+
"",
160+
"TXVsdGlFU0RUTkZUVHJhbnNmZXI=",
161+
"AAAAAAAAAAAFAEZl7brKa3YxG9sH7MKaA7wOCZydD9Y=",
162+
"Ag==",
163+
"RUdMRC0wMDAwMDA=",
164+
"",
165+
"JxA=",
166+
"Uk9TRVRUQS0xNTk3Yjg=",
167+
"",
168+
"AQ==",
169+
"cGF5YWJsZV9hbGxfdHJhbnNmZXJz"
170+
]
171+
}
172+
]
173+
},
174+
"status": "success",
175+
"tokens": [
176+
"EGLD-000000",
177+
"ROSETTA-1597b8"
178+
],
179+
"esdtValues": [
180+
"10000",
181+
"1"
182+
],
183+
"receivers": [
184+
"erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq",
185+
"erd1qqqqqqqqqqqqqpgqgej7mwk2ddmrzx7mqlkv9xsrhs8qn8yapltqs8a2qq"
186+
],
187+
"receiversShardIDs": [
188+
2,
189+
2
190+
],
191+
"operation": "MultiESDTNFTTransfer",
192+
"function": "payable_all_transfers",
193+
"initiallyPaidFee": "396105000000000",
194+
"fee": "396105000000000",
195+
"chainID": "T",
196+
"version": 1,
197+
"options": 0
198+
}
199+
},
200+
"error": "",
201+
"code": "successful"
202+
}"#;
203+
204+
let tx_on_network: TransactionOnNetwork = serde_json::from_str::<TransactionInfo>(data)
205+
.unwrap()
206+
.data
207+
.unwrap()
208+
.transaction;
209+
let tx_response = network_response::parse_tx_response(tx_on_network, ReturnCode::Success);
210+
211+
let expected: Vec<u8> = vec![
212+
0, 0, 0, 4, 69, 71, 76, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 39, 16, 0, 0, 0, 14, 82,
213+
79, 83, 69, 84, 84, 65, 45, 49, 53, 57, 55, 98, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
214+
];
215+
assert_eq!(tx_response.out, vec![expected]);
216+
}

sdk/core/src/data/transaction.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub struct ApiSmartContractResult {
148148
pub code_metadata: Option<String>,
149149
pub return_message: Option<String>,
150150
pub original_sender: Option<String>,
151+
pub logs: Option<ApiLogs>,
151152
}
152153

153154
#[derive(Debug, Clone, Serialize, Deserialize)]

0 commit comments

Comments
 (0)