+
Skip to content

@ton/sandbox: State changes not persisted after self-call in a transaction chain #135

@Leepey

Description

@Leepey

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

Шаги для воспроизведения:

  1. Создайте смарт-контракт со следующей логикой в recv_internal:

    • При получении сообщения с пустым телом (in_msg_body.slice_empty?()).
    • Контракт изменяет несколько глобальных переменных (например, total_deposits += 1).
    • Вызывает save_data() для сохранения изменений.
    • Отправляет сообщение самому себе, чтобы создать action и заставить TVM сохранить состояние.
  2. Напишите тест, который:

    • Отправляет транзакцию на контракт.
    • Проверяет, что состояние изменилось (например, через 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);
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载