-
Notifications
You must be signed in to change notification settings - Fork 64
Description
Description
Здравствуйте!
Я столкнулся с критической проблемой при тестировании смарт-контрактов в среде @ton/sandbox
, которая мешает корректной работе стандартного паттерна фиксации состояния.
Краткое описание проблемы:
Изменения состояния (глобальные переменные, словари), сделанные в одной транзакции, не сохраняются, если для их фиксации используется самовызов (отправка сообщения самому себе). В реальной сети этот паттерн работает корректно.
Окружение:
"@ton/sandbox": ">=0.37.0",
"@ton/test-utils": ">=0.11.0",
"@types/jest": "^30.0.0",
"@types/node": "^22.17.2",
"jest": "^30.0.5",
"prettier": "^3.6.2",
"@ton/ton": ">=15.3.1 <16.0.0",
"@ton/crypto": "^3.3.0",
"ts-jest": "^29.4.1",
"ts-node": "^10.9.2",
"typescript": "^5.9.2",
"@ton/tolk-js": ">=1.0.0",
"@tact-lang/compiler": ">=1.6.13 <2.0.0",
"@ton-community/func-js": ">=0.10.0"
- ОС: Windows
Steps to Reproduce
Шаги для воспроизведения:
-
Создайте смарт-контракт со следующей логикой в
recv_internal
:- При получении сообщения с пустым телом (
in_msg_body.slice_empty?()
). - Контракт изменяет несколько глобальных переменных (например,
total_deposits += 1
). - Вызывает
save_data()
для сохранения изменений. - Отправляет сообщение самому себе, чтобы создать
action
и заставить TVM сохранить состояние.
- При получении сообщения с пустым телом (
-
Напишите тест, который:
- Отправляет транзакцию на контракт.
- Проверяет, что состояние изменилось (например, через get-метод
get_basic_stats
).
Ожидаемое поведение:
- Транзакция от пользователя (Tx 0) создает
action
. - Транзакция от контракта к себе (Tx 1) успешно обрабатывает этот
action
. - Изменения состояния, сделанные в Tx 0, сохраняются в хранилище контракта.
- Get-метод после этого возвращает обновленные данные.
Фактическое поведение:
- Логи транзакций показывают, что Tx 0 успешно создала
action
(Total Actions: 1
). - Tx 1 успешно обрабатывается (
Success: true
,Exit Code: 0
). - Однако get-методы возвращают старые данные, как будто
save_data()
никогда не вызывался. Состояние не сохраняется.
Environment
No response
Logs or Screenshots
Логи транзакций:
Вот логи из @ton/sandbox
, которые демонстрируют проблему:
--- Transaction 0 ---
From: N/A (e.g., deploy)
To: 0x... (user address)
Success: true
Exit Code: 0
Total Actions: 1 <-- Важно: Действие создано
--- Transaction 1 ---
From: 0x... (user address)
To: 0x... (contract address)
Success: true
Exit Code: 0
Total Actions: 0 <-- Важно: Самовызов обработан, но состояние не сохранилось
Код контракта (FunC):
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
;; ... проверки ...
load_data();
if (in_msg_body.slice_empty?()) {
return handle_simple_deposit(msg_value, sender);
}
;; ... остальная логика ...
}
() handle_simple_deposit(int msg_value, slice sender) impure {
;; ... проверки ...
// Изменяем состояние
total_deposits += 1;
save_data();
// Отправляем сообщение самому себе, чтобы зафиксировать изменения
send_raw_message(begin_cell()
.store_uint(0x18, 6)
.store_slice(my_address())
.store_coins(0)
.store_uint(op_self_call, 32) // op_self_call = 0x73656c66
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.end_cell(), 64);
}
() recv_internal(...) {
// ...
int op = in_msg_body~load_uint(32);
if (op == op_self_call) {
return (); // Получили самовызов, ничего не делаем.
}
// ...
}
Код теста (TypeScript/Jest):
it('should accept deposits with subsidy', async () => {
const initialStats = await mixton.getBasicStats();
await mixton.sendDeposit(user.getSender(), toNano('10.0'));
const newStats = await mixton.getBasicStats();
// Этот expect падает
expect(newStats.totalDeposits).toBe(initialStats.totalDeposits + 1);
});