有限状态机用于无信任托管:我们如何在 TON 上构建去中心化市场

发布: (2026年2月28日 GMT+8 12:21)
10 分钟阅读
原文: Dev.to

Source: Dev.to

购买 Telegram 频道很不靠谱。购买任何社交媒体资产都很不靠谱。卖家可能拿走你的钱后消失。买家可能收到资产后争议付款。每个中心化的托管服务都是单点故障——也是单点信任。

我们构建了 ITOhub 来通过链上托管、预言机确认的转账,以及完全由 FunC 智能合约中的 有限状态机 (FSM) 管理的交易生命周期来解决这个问题。

下面是关于为何有限状态机是链上业务逻辑的完美模式以及我们是如何实现它们的概述。

为什么使用有限状态机?

每个市场交易都有可预测的流程:

created → funded → asset transferred → confirmed → completed (or disputed)

这是教材中的 FSM 场景。

在智能合约中编码流程的好处

  • 每一次状态转换都在链上 —— 完全可审计。
  • 无效的转换不可能发生 —— 合约会拒绝它们。
  • 对正常路径不需要人工仲裁
  • 争议解决规则明确 —— 不靠“感觉”。

FSM 图

┌─────────┐    fund()    ┌──────────┐   confirm_transfer()   ┌────────────────┐
│ CREATED │────────────▶│  FUNDED  │──────────────────────▶│ TRANSFER_SENT │
└─────────┘              └──────────┘                        └───────┬───────┘
     │                        │                                      │
     │ cancel()               │ cancel()              oracle_confirm()
     │                        │                                      │
     ▼                        ▼                                      ▼
┌─────────┐              ┌──────────┐                       ┌───────────────┐
│CANCELLED│              │ REFUNDED │                       │   COMPLETED   │
└─────────┘              └──────────┘                       └───────────────┘

                                                              auto‑release
                                                              vault → seller

智能合约 (FunC)

TON 的智能合约运行在 TVM(TON 虚拟机)上。我们选择了 FunC —— 相比 Tact 更底层,但它让我们能够精确控制 gas 消耗和消息处理。

状态编码

;; Deal states
const int STATE_CREATED       = 0;
const int STATE_FUNDED        = 1;
const int STATE_TRANSFER_SENT = 2;
const int STATE_COMPLETED     = 3;
const int STATE_CANCELLED     = 4;
const int STATE_REFUNDED      = 5;
const int STATE_DISPUTED      = 6;

;; Deal storage layout
;; [state:3][buyer:267][seller:267][amount:120][asset_id:256][oracle:267][created_at:32][timeout:32]

状态转移守卫

每个操作在执行前都会检查当前状态。这是核心的有限状态机(FSM)强制实现:

() assert_state(int current, int expected) impure inline {
  throw_unless(400, current == expected);  ;; 400 = invalid state transition
}

() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
  int op = in_msg_body~load_uint(32);

  ;; Load deal state
  var ds = get_data().begin_parse();
  int state = ds~load_uint(3);
  slice buyer  = ds~load_msg_addr();
  slice seller = ds~load_msg_addr();
  int amount   = ds~load_coins();
  ;; ... rest of fields

  if (op == op::fund) {
    assert_state(state, STATE_CREATED);
    ;; Verify sender is buyer
    throw_unless(401, equal_slices(sender, buyer));
    ;; Verify sent amount matches deal
    throw_unless(402, msg_value >= amount);

    ;; Transition: CREATED → FUNDED
    save_state(STATE_FUNDED, buyer, seller, amount, ...);
    return ();
  }

  if (op == op::confirm_transfer) {
    assert_state(state, STATE_FUNDED);
    ;; Only seller can confirm they've initiated the transfer
    throw_unless(401, equal_slices(sender, seller));

    ;; Transition: FUNDED → TRANSFER_SENT
    save_state(STATE_TRANSFER_SENT, buyer, seller, amount, ...);
    return ();
  }

  if (op == op::oracle_confirm) {
    assert_state(state, STATE_TRANSFER_SENT);
    ;; Only designated oracle can confirm
    throw_unless(401, equal_slices(sender, oracle));

    ;; Transition: TRANSFER_SENT → COMPLETED
    ;; Release vault to seller
    send_raw_message(build_transfer_msg(seller, amount), 64);
    save_state(STATE_COMPLETED, buyer, seller, amount, ...);
    return ();
  }

  ;; ... cancel, dispute, timeout handlers
}

金库模式

资金不放在共享池中——每笔交易都有自己的合约实例(由工厂部署的子合约)。这意味着:

;; Factory contract deploys a new deal contract per transaction
() create_deal(slice buyer, slice seller, int amount, int asset_id, slice oracle) impure {
  cell state_init = build_deal_state_init(buyer, seller, amount, asset_id, oracle);
  slice deal_address = calculate_address(state_init);

  ;; Deploy deal contract with initial state
  send_raw_message(
    begin_cell()
      .store_uint(0x18, 6)          ;; external inbound message header
      .store_slice(deal_address)   ;; destination address
      .store_coins(50000000)        ;; 0.05 TON for gas
      .store_uint(6, 107)          ;; state_init flag
      .store_ref(state_init)        ;; contract code+data
      .store_uint(op::init, 32)    ;; init op
    .end_cell(),
    1
  );
}

为什么使用每笔交易的合约?

  • 隔离 – 一个交易中的漏洞无法抽干其他交易的资金。
  • 确定性金库 – 每个合约单一用途且可预测。

Source:

预言机层

最棘手的部分:如何验证一个 Telegram 频道真的已经转让?

我们构建了一个确认预言机,其功能如下:

  • 监控资产(例如,通过 Bot API 检查 Telegram 频道的管理员列表)
  • 在链上发送确认,当转让被验证后
  • 设有超时——如果预言机在 N 小时内未确认,买家可以发起争议
// Oracle service (NestJS)
@Injectable()
export class TelegramTransferOracle {
  constructor(
    private readonly tonClient: TonClient,
    private readonly httpService: HttpService,
  ) {}

  // Called by off‑chain worker when transfer is detected
  async confirmTransfer(dealAddress: string, proof: TransferProof): Promise<void> {
    // Build on‑chain message
    const msg = beginCell()
      .storeUint(op::oracle_confirm, 32)
      .storeUint(proof.timestamp, 32)
      .storeSlice(proof.signature)
      .endCell();

    await this.tonClient.sendExternalMessage({
      to: dealAddress,
      body: msg,
      value: 0, // no TON needed, just a signal
    });
  }

  // Periodic health‑check – if timeout passes, expose dispute endpoint
}

TL;DR

  • FSM 为我们提供了数学可证明的流程 —— 每一次状态变更都在链上强制执行。
  • FunC 让我们能够以细粒度的 gas 控制实现该流程
  • 每笔交易的金库合约将资金隔离,消除单点故障。
  • 链下预言机将现实资产的转移桥接回区块链,完成无信任的托管循环。

这就是 ITOhub 如何消除数字资产市场中的信任问题。

TransferOracle

class TransferOracle {
  async verifyChannelTransfer(deal: Deal): Promise<boolean> {
    const channel = await this.telegramBot.getChat(deal.assetId);
    const admins = await this.telegramBot.getChatAdministrators(deal.assetId);

    // Check if buyer is now an admin with full rights
    const buyerAdmin = admins.find(
      a => a.user.id === deal.buyerTelegramId && a.status === 'creator',
    );

    if (buyerAdmin) {
      // Send on‑chain confirmation
      await this.tonService.sendOracleConfirmation(deal.contractAddress);
      return true;
    }

    return false;
  }

  @Cron('*/60 * * * * *') // Check every minute
  async pollPendingTransfers() {
    const pending = await this.dealRepo.findByState('TRANSFER_SENT');
    for (const deal of pending) {
      await this.verifyChannelTransfer(deal);
    }
  }
}

预言机信任最小化

预言机只能执行两项操作:确认不确认。它不能:

  • 转移资金
  • 更改交易参数
  • 单方面取消已融资的交易

如果预言机被攻破,最坏的情况是交易卡住——这将由超时机制自动解决。

Telegram 小程序

前端以 Telegram Mini App(TMA)的形式运行,为用户提供原生的使用体验:

// Deal creation flow
async function createDeal(params: DealParams) {
  // 1. Create deal record in backend
  const deal = await api.post('/deals', {
    assetType: 'telegram-channel',
    assetId: params.channelId,
    price: params.price,
    sellerTelegramId: params.sellerId,
  });

  // 2. Deploy on‑chain contract via TonConnect
  const tx = {
    validUntil: Math.floor(Date.now() / 1000) + 600,
    messages: [
      {
        address: FACTORY_CONTRACT,
        amount: toNano('0.1').toString(), // Deploy gas
        payload: buildCreateDealPayload(deal),
      },
    ],
  };

  await tonConnect.sendTransaction(tx);

  // 3. Redirect to deal tracking page
  navigate(`/deals/${deal.id}`);
}

生产经验教训

  1. FSMs make auditing trivial – 每笔交易在链上都有完整的状态历史。
  2. Per‑deal contracts are worth the deployment cost – 在 TON 上,部署一个合约的费用约为 0.05 TON(约 $0.10)。
  3. Oracle design is the real security challenge – 我们正向多 Oracle 模型(2‑of‑3 确认)转变。
  4. Timeouts prevent deadlocks – 每个状态都有超时设置,允许对方取消或提出争议。

数字

  • 交易生命周期: CREATED → COMPLETED 平均 2.3 小时
  • 零资金损失 来自合约漏洞
  • 争议率: < 5 %(大多数通过预言机超时解决)

完整的交易审计可通过 TON Explorer 查看。

何时使用此模式

FSM + 链上保险库模式适用于以下任何市场:

  • 资产可以通过程序验证(频道所有权、域名转让、NFT 持有)
  • 双方都需要保护(不仅仅是买方)
  • 正常流程常见(争议是例外)

在以下情况下效果不佳:

  • 资产转移验证需要人工判断
  • 资产为实物(需要物流预言机)
  • 交易量要求亚秒级最终性(合约部署会增加延迟)

开源计划

我们计划将核心 FSM 托管合约开源,作为可复用的模板。如果您正在 TON 上构建市场并希望提前获取,请查看 docs.itohub.org 的文档或在 Telegram bot 中联系我们。

Gerus Lab 构建去中心化协议、AI 驱动的产品以及 Telegram 原生应用。从 NFT 质押到 DCA 机器人再到托管系统——我们将复杂的架构转化为生产软件。

0 浏览
Back to Blog

相关文章

阅读更多 »

当工作成为心理健康风险时

markdown !Ravi Mishrahttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fu...