Learning Platform
Глоссарий Troubleshooting
Урок 04.04 · 18 мин
Продвинутый
State MachineMulti-step OperationsState TransitionsDesign Pattern

State Machines для TON контрактов

Зачем State Machine

В синхронном коде multi-step операция — это функция с последовательными вызовами. В TON каждый шаг — отдельная транзакция. Между шагами контракт должен помнить, на каком этапе находится.

State Machine — формальный способ описать это:

State Machine: DEX Swap
IDLE
AWAITING_TOKEN
EXECUTING
COMPLETED

Из любого состояния: bounce → REFUNDING → IDLE

Реализация State Machine

// Состояния контракта
const STATE_IDLE = 0;
const STATE_AWAITING_TOKEN = 1;
const STATE_EXECUTING = 2;
const STATE_REFUNDING = 3;

// State в persistent storage
self.state = STATE_IDLE;
self.pending_swap = null;

recv_internal(msg) {
  // Bounce handler (из любого state)
  if (msg.bounced) {
    handle_bounce(msg);
    return;
  }
  
  // State-dependent message handling
  switch (self.state) {
    case STATE_IDLE:
      if (op == op::swap_request) {
        self.pending_swap = {sender, amount, min_output, deadline};
        self.state = STATE_AWAITING_TOKEN;
        // Send transfer request to sender's Jetton wallet
        send(sender_wallet, gas, transfer_msg);
      }
      break;
      
    case STATE_AWAITING_TOKEN:
      if (op == op::transfer_notification) {
        // Token received, execute swap
        let output = calculate_output(received_amount);
        if (output < self.pending_swap.min_output) {
          // Slippage exceeded — refund
          self.state = STATE_REFUNDING;
          send(sender_wallet, gas, refund_msg);
        } else {
          self.state = STATE_EXECUTING;
          send(output_wallet, gas, transfer_output_msg);
        }
      }
      if (op == op::timeout) {
        // Deadline passed — refund
        self.state = STATE_REFUNDING;
        send(sender_wallet, gas, refund_msg);
      }
      break;
      
    case STATE_EXECUTING:
      if (op == op::transfer_notification) {
        // Output sent successfully
        self.state = STATE_IDLE;
        self.pending_swap = null;
      }
      break;
      
    case STATE_REFUNDING:
      if (op == op::transfer_notification) {
        // Refund complete
        self.state = STATE_IDLE;
        self.pending_swap = null;
      }
      break;
  }
}

Design Rules для State Machines

Rule 1: Явные состояния

Каждое состояние — exactly one enum value в storage. Не полагайтесь на implicit state (наличие/отсутствие данных).

Rule 2: Все transitions документированы

Transition Table:
IDLE + swap_request → AWAITING_TOKEN
AWAITING_TOKEN + transfer_notification → EXECUTING
AWAITING_TOKEN + timeout → REFUNDING
AWAITING_TOKEN + bounce → REFUNDING
EXECUTING + transfer_notification → IDLE (complete)
EXECUTING + bounce → REFUNDING
REFUNDING + transfer_notification → IDLE (refunded)

Rule 3: Timeouts для каждого pending state

Любое pending state должно иметь deadline. Если ответ не пришёл за N блоков — timeout → compensation.

Rule 4: Bounce из любого state

Bounce handler должен работать из любого состояния:

handle_bounce(msg) {
  if (self.state == STATE_AWAITING_TOKEN || 
      self.state == STATE_EXECUTING) {
    self.state = STATE_REFUNDING;
    // Вернуть средства original sender
    refund(self.pending_swap.sender, self.pending_swap.amount);
  }
}

Anti-pattern: Implicit State

[NO] Implicit state (опасно):
if (self.pending_amount > 0 && self.pending_sender != null) {
  // "видимо, мы ждём token"
}

[OK] Explicit state (безопасно):
if (self.state == STATE_AWAITING_TOKEN) {
  // Точно знаем, что ждём token
}
TIP

Tip: рисуйте state diagram перед кодом

Для каждого контракта нарисуйте state diagram со всеми transitions. Включите bounce и timeout transitions. Если diagram выглядит сложно — возможно, контракт делает слишком много и нужно разделить на несколько.

Проверка знанийKnowledge check
ОтветAnswer

Проверьте понимание

Результат: 0 из 0
Аналитический
Вопрос 1 из 2. DEX контракт использует implicit state: 'if (pending_amount > 0) { // видимо, ждём token }'. Почему это anti-pattern?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5