Skip to content

Commit d03782d

Browse files
committed
Add stTON burn message handling
1 parent 379f02e commit d03782d

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

tondb-scanner/src/InterfaceDetectors.hpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -552,8 +552,18 @@ class JettonWalletDetector: public InterfaceDetector<JettonWalletData> {
552552
void parse_burn_impl(JettonWalletData contract_data, schema::Transaction transaction, td::Ref<vm::CellSlice> cs, td::Promise<JettonBurn> promise) {
553553
tokens::gen::InternalMsgBody::Record_burn burn_record;
554554
if (!tlb::csr_unpack_inexact(cs, burn_record)) {
555-
promise.set_error(td::Status::Error(ErrorCode::EVENT_PARSING_ERROR, "Failed to unpack burn"));
556-
return;
555+
// handle stTON burn message format
556+
// burn#595f07bc query_id:uint64 jetton_amount:Coins = InternalMsgBody;
557+
if (contract_data.jetton == "0:CD872FA7C5816052ACDF5332260443FAEC9AACC8C21CCA4D92E7F47034D11892") {
558+
cs.write().skip_first(32); // opcode
559+
if (!cs.write().fetch_ulong_bool(64, burn_record.query_id) || !tokens::gen::t_VarUInteger_16.fetch_to(cs.write(), burn_record.amount)) {
560+
promise.set_error(td::Status::Error(ErrorCode::EVENT_PARSING_ERROR, "Failed to unpack stTON burn"));
561+
return;
562+
}
563+
} else {
564+
promise.set_error(td::Status::Error(ErrorCode::EVENT_PARSING_ERROR, "Failed to unpack burn"));
565+
return;
566+
}
557567
}
558568

559569
JettonBurn burn;
@@ -580,14 +590,20 @@ class JettonWalletDetector: public InterfaceDetector<JettonWalletData> {
580590
promise.set_error(td::Status::Error(ErrorCode::EVENT_PARSING_ERROR, "Failed to unpack burn amount"));
581591
return;
582592
}
583-
auto response_destination = convert::to_raw_address(burn_record.response_destination);
584-
if (response_destination.is_error()) {
585-
promise.set_error(response_destination.move_as_error());
586-
return;
593+
if (burn_record.response_destination.not_null()) {
594+
auto response_destination = convert::to_raw_address(burn_record.response_destination);
595+
if (response_destination.is_error()) {
596+
promise.set_error(response_destination.move_as_error());
597+
return;
598+
}
599+
burn.response_destination = response_destination.move_as_ok();
600+
} else {
601+
burn.response_destination = "addr_none";
587602
}
588-
burn.response_destination = response_destination.move_as_ok();
589603
// since some messages don't have maybe ref cell at all we can ignore error here
590-
burn_record.custom_payload_cell.write().fetch_maybe_ref(burn.custom_payload);
604+
if (burn_record.custom_payload_cell.not_null()) {
605+
burn_record.custom_payload_cell.write().fetch_maybe_ref(burn.custom_payload);
606+
}
591607

592608
promise.set_value(std::move(burn));
593609
}

tondb-scanner/test/tests.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,64 @@ TEST(TonDbScanner, JettonWalletDetector_parse_burn_without_custom_payload) {
158158
scheduler.stop();
159159
}
160160

161+
TEST(TonDbScanner, JettonWalletDetector_parse_burn_without_response_destination) {
162+
/**
163+
* The same situation as in the previous test - burn message doesn't have some required fields.
164+
* stTON (bemo) burn message doesn't require response_destination field at all and
165+
* in the documentation (https://docs.bemo.fi/developers/unstake) one can find a TL-B schema of the burn message:
166+
* burn#595f07bc query_id:uint64 jetton_amount:Coins = InternalMsgBody;
167+
*
168+
* Since stTON is quite popular, it is worth to add it as an exception to the rule.
169+
*/
170+
td::actor::Scheduler scheduler({1});
171+
auto watcher = td::create_shared_destructor([] { td::actor::SchedulerContext::get()->stop(); });
172+
173+
// message payload for tx bea8befb0d6a928fa00e4ec4e6035b760ec12d4b339dd6cb2d53077fbf99c87f
174+
auto message_payload = vm::load_cell_slice_ref(vm::std_boc_deserialize(td::base64_decode(
175+
td::Slice("te6ccgEBAQEAFAAAI1lfB7wAAAAAAAAAAFU2CBF7qA==")).move_as_ok()).move_as_ok());
176+
177+
auto transaction = schema::Transaction();
178+
transaction.account = block::StdAddress(std::string("EQCbVZsB2jOe0DGc0DSUA-gxqzfuAjBI7mCvIxuYmWkzBNl-")); // jetton wallet
179+
transaction.in_msg = std::make_optional(schema::Message());
180+
transaction.in_msg->source = "0:E27383455DD29192D213773AD731540C1D83CE03BB9C9D59A13A9800335EAC22"; // owner
181+
182+
td::actor::ActorId<InsertManagerInterface> insert_manager;
183+
td::actor::ActorOwn<JettonWalletDetector> jetton_wallet_detector;
184+
td::actor::ActorOwn<InterfaceManager> interface_manager;
185+
td::actor::ActorOwn<JettonMasterDetector> jetton_master_detector;
186+
187+
// prepare jetton metadata
188+
std::unordered_map<std::string, JettonWalletData> cache;
189+
JettonWalletData jetton_master;
190+
jetton_master.jetton = "0:CD872FA7C5816052ACDF5332260443FAEC9AACC8C21CCA4D92E7F47034D11892";
191+
cache.emplace(std::string("0:9B559B01DA339ED0319CD0349403E831AB37EE023048EE60AF231B9899693304"), jetton_master);
192+
193+
scheduler.run_in_context([&] {
194+
interface_manager = td::actor::create_actor<InterfaceManager>("interface_manager", insert_manager);
195+
jetton_master_detector = td::actor::create_actor<JettonMasterDetector>("jetton_master_detector", interface_manager.get(), insert_manager);
196+
197+
jetton_wallet_detector = td::actor::create_actor<JettonWalletDetector>("jetton_wallet_detector",
198+
jetton_master_detector.get(), interface_manager.get(), insert_manager, cache);
199+
200+
auto P = td::PromiseCreator::lambda([&transaction, &jetton_master](td::Result<JettonBurn> R) {
201+
CHECK(R.is_ok());
202+
auto burn = R.move_as_ok();
203+
ASSERT_EQ(transaction.in_msg->source.value(), burn.owner);
204+
ASSERT_EQ(convert::to_raw_address(transaction.account), burn.jetton_wallet);
205+
ASSERT_EQ(jetton_master.jetton, burn.jetton_master);
206+
ASSERT_EQ(0, burn.query_id);
207+
ASSERT_TRUE(burn.custom_payload.is_null());
208+
ASSERT_EQ(std::string("addr_none"), burn.response_destination);
209+
CHECK(td::BigIntG<257>(358101358522) == **burn.amount.get());
210+
});
211+
td::actor::send_closure(jetton_wallet_detector, &JettonWalletDetector::parse_burn, transaction, message_payload, std::move(P));
212+
watcher.reset();
213+
});
214+
215+
scheduler.run(10);
216+
scheduler.stop();
217+
}
218+
161219

162220
TEST(TonDbScanner, JettonWalletDetector) {
163221
td::actor::Scheduler scheduler({1});

0 commit comments

Comments
 (0)