全部文章
7 分钟阅读

TON 合约 get 方法怎么调?run_get_method 一次搞定,不用 SDK

用 run_get_method 调用 TON 合约的任意 get 方法:seqno、get_jetton_data、get_sale_data,参数与栈怎么传、exit_code 怎么读、返回栈怎么解析——全程不用 SDK。

run_get_methodTON get 方法TVM exit_codeTON 不用 SDKTON MCPget_jetton_data

你打开区块浏览器,只是想在发交易前查一下钱包的 seqno。或者你需要某个 jetton 的 total_supply。又或者是市场上某个 NFT 挂单的数据。结果呢,你又一次 npm i @ton/ton,起一个 TonClient,四处找一个还能用的 lite server 端点,研究怎么把地址塞进 beginCell().storeAddress(),最后还得手动解析返回的 BOC。三十行代码——就为了一个合约本来就免费给你的数字。

问题不在 TON。问题在于,在你和一次简单的只读调用之间,横着整整一层 SDK:要装、要配、下次升级还不能弄坏。而如果你是在给 TON 接一个 AI 智能体(Claude、Cursor、ChatGPT/Codex),手写这一层就更荒唐了——智能体只需要把方法调起来就行。

下面就来看:如何通过 run_get_method 调用 TON 合约的任意 get 方法,一行 SDK 客户端代码都不用写。

TON 合约的 get 方法是什么,为什么调它通常还要拖上一整套 SDK

get 方法就是智能合约的只读函数。钱包给出 seqno,jetton 主合约给出 get_jetton_data,NFT 出售合约给出 get_sale_data,STON.fi/DeDust 的池子给出 get_pool_data。关键词是只读

  • 方法不改变合约的任何状态;
  • 不需要签名,也不消耗用户的 gas;
  • 它在 lite server 或 TVM(TON Virtual Machine)模拟器里执行,而不是靠发一笔交易。

也就是说,调用 get 方法不是一笔交易。这里没有东西要签名,没有费用要付,也不用等区块确认。本质上它就是一个函数:“从链上正在运行的合约里读一个值”。

但要“按老办法”调用它,就得先备齐一整套外围组件:SDK(tontonwebtonutils)、自己写一个连到 lite server 的 ADNL 客户端,或者套一层像 toncenter 这样的 HTTP 封装,再加上手动把入参打包成 cell、手动解析返回的 BOC。为了一次读取,要动的零件太多了。而且每个零件都是一个故障点:全局配置里的公共 lite server 是共享且限流的,负载一高就返回 not ready,或者干脆 ADNL 超时;公共 HTTP API 一旦超出限额就返回 429 Too Many Requests(不带 key 大约每秒一个请求)。

run_get_method:一个工具,调用任意合约的任意只读方法

run_get_method 是一个 MCP 工具,可以调用任意 TON 合约的任意只读 get 方法。你传入合约地址、方法名和参数列表——工具通过 TVM 在链上执行该方法,并返回输出栈。

而你不需要

  • 安装和升级 SDK(ton/tonweb/tonutils);
  • 自己写一个 ADNL 客户端;
  • 手动把参数打包成 cell、再手动解析输出的 BOC。

run_get_method 属于 TONNode 的读取工具集——TONNode 是面向 TON 的 hosted MCP 服务器。MCP(Model Context Protocol)是 AI 智能体调用工具所遵循的标准协议。整套读取工具都可以通过 npx -y @tonnode/mcp 本地免费使用(公开配置,无需绑卡),也可以用 Bearer key 访问 hosted 端点 https://mcp.tonnode.io/mcp。现在就能试,不用买任何东西。

参数与栈:怎么传入参数,怎么解析 TVM 的输出

TVM 是基于栈的。打个比方:你在桌上摆几张写着输入值的“卡片”,方法把它们拿走、跑完,再把写着结果的“卡片”摆回桌上。

输入以值的形式压入 TVM 栈。通常是:

  • int——整数(比如索引、raw 单位表示的金额、query id);
  • 地址 slice——打包进 cell slice 的地址;
  • cell——任意的数据 cell。

输出以一个值栈返回:intslicecelltuple。智能体按位置解析它——第一个值、第二个、第三个。比如 get_jetton_data 依次返回:total_supply(int)、mintable 标志、admin(slice 地址)、content(cell)以及钱包代码(cell)。位置决定含义——这是合约自身 ABI 约定的一部分。

很多常用方法(seqnoget_jetton_data)根本不需要输入——参数栈是空的。只有当方法要按某个 key 去查东西时才会出现参数:比如根据持有人地址计算 jetton 钱包的地址。

一个重要细节:run_get_method 返回的是以 raw 单位表示的原始栈。数字原样返回,不会按 decimals 换算,cell 也不会被转成可读字符串。这很灵活,但要求你清楚自己在读什么——这一点我们在 get_jetton_info 那一节再展开。

exit_code:怎么判断方法跑成功了

每次 get 方法调用都有一个 exit_code——TVM 的退出码。只有调用本身成功了,去解析输出栈才有意义。get 方法的规则有点反直觉,值得记住:

  • exit_code 为 0 和 1 都是成功(两者都算正常结束,所以看到 1 不用慌);
  • exit_code > 1 是错误,此时输出栈不可信。

常见的错误码:

exit_code 含义
2 stack underflow——压栈的参数比方法期望的少
4 整数溢出 / 除以零
11 通常是调用了不存在的方法(方法名拼错了)
13 out of gas

实践中:拿到 2——检查参数是否传全、顺序对不对。拿到 11——多半是方法名拼错了,或者这个合约压根没实现它。拿到 13——方法太重,撞上了 gas 上限。

智能体实战示例:seqno、get_jetton_data、get_sale_data

最舒服的地方在于,调用 run_get_method 的不是人,而是智能体。你用一句话把任务说清楚,智能体自己挑地址、方法名和参数,拿到栈之后再把各个字段解释给你听。

发交易前的 seqno。 seqno 是钱包出站交易的序号,组装转账之前要先读它:没有它,消息发不出去。

给智能体的提示词:“在我的钱包 UQD… 上调用 seqno,告诉我当前的序号。”

智能体不带参数调用 run_get_method,在输出栈上拿到一个 int,然后把这个数字给你。

get_jetton_data——jetton 主合约的元数据。 该方法返回 total_supplymintable 标志、admin 地址和 content

给智能体的提示词:“对这个 USDT 主合约调用 get_jetton_data,把各个字段拆开讲清楚。”

智能体带上主合约地址和方法名 get_jetton_data 调用 run_get_method,读取栈并解释每个位置的含义。这里有个重要的坑——下面就说。

get_sale_data——NFT 挂单数据。 这不是什么单独的 NFT 工具,还是同一个 run_get_method:你读的是市场上出售合约自己的 get 方法。get_sale_data 返回价格、卖家、成交状态——很方便判断这个 NFT 是已经卖掉了,还是还挂在那儿。

给智能体的提示词:“从这个出售合约读一下 get_sale_data,告诉我以 GRAM 计价的价格。”

你一行客户端代码都不用写。智能体调一个工具就够了。其他常用方法——get_wallet_data(jetton 钱包数据)、get_pool_data(STON.fi/DeDust 的池子)——调用方式完全一样:方法名、地址,需要的话再加参数。

get_jetton_data 还是 get_jetton_info:什么时候需要人类可读的结果

这里就是最大的坑。run_get_method 返回的是原始栈:get_jetton_data 给你的 total_supply 是一个以 raw 单位计的巨大整数,而 content 是一个 cell,名称和符号还得自己从里面掏出来。没有“USDT”,没有“6 decimals”,也没有好看的数字——这就是底层的 TVM 输出。如果你要的正是对主合约的底层访问,或者某个非标准字段,那这个工具就是对的。

但如果你的目标只是拿到现成的 jetton 名称、符号和 decimals,就别折腾 cell 解析了。为此有一个独立的工具——get_jetton_info:它直接给出已经解析好的名称、符号、decimals 和发行量。

为什么这点很关键:decimals 决定了 raw 单位到真实金额的换算。TON 网络上的 USDT decimals = 6,大多数 jetton 是 9。如果你拿原始 get_jetton_data 里的 total_supply 而不除以 10^decimals,得到的数字会和真实值差出一百万倍或十亿倍。关于这个坑的详细拆解,见 TON 上 jetton 的 decimals

规则很简单:

  • 需要合约的原始字段(发行量、admin、content、任意自定义方法)→ run_get_method + get_jetton_data
  • 需要现成的、人类可读的 jetton 信息(名称、符号、decimals)→ get_jetton_info

别把 get_jetton_data 的 raw 数字和 get_jetton_info 的现成结果混为一谈。

两个好用的辅助工具:parse_address 和 get_account_state

调用方法之前,最好先把地址理顺,并确认合约还活着:

  • parse_address——在 EQ/UQ/raw 各种格式之间规范化地址,离线完成,不访问网络。TON 的地址格式有好几种,而且并不是每个方法都能接受所有格式;在填进参数之前,把用户给的 EQ… 转成需要的形式很方便。
  • get_account_state——检查合约的状态、标志位和最后一笔交易。如果账户没有部署(uninit)或者被冻结,任何 get 方法都注定会失败——提前查一下状态,比事后对着一个莫名其妙的 exit_code 发呆划算得多。

怎么接入并调用——一行客户端代码都不用

有两条路。第一条是本地免费,按公开配置使用整套读取工具,无需绑卡:

{
  "mcpServers": {
    "ton": {
      "command": "npx",
      "args": ["-y", "@tonnode/mcp"]
    }
  }
}

@tonnode/mcp 包是开源的(MIT),发布在 npm 和 GitHub(tonnode/mcp)上,走 TON 原生的 ADNL 协议,没有 HTTP 中间层。关于免费这条路有一篇单独的拆解——免费用上 TON 的 MCP

第二条是 hosted 端点,带自己的 key,吞吐有保障:

{
  "mcpServers": {
    "ton": {
      "type": "http",
      "url": "https://mcp.tonnode.io/mcp",
      "headers": { "Authorization": "Bearer tn_live_…" }
    }
  }
}

所有套餐都能用上全部 16 个工具——你付费买的只是吞吐量。免费的 Hobby key(60 请求/分钟)登录后立刻发放,无需绑卡。

接入之后,整套读取工具——包括 run_get_methodget_jetton_infoparse_addressget_account_state——就像普通工具一样暴露给智能体。调用起来就是聊天里的一句话:“对这个 DeDust 池子调用 get_pool_data”、“读一下这个钱包的 seqno”——智能体会自己选中 run_get_method、组装参数,并把字段解释清楚。而如果你的任务是查 USDT 余额,还有一条现成的单命令路径:一次调用查出 TON 上的 USDT 余额


领一个免费的 Hobby key,直接从智能体里调用 run_get_methodtonnode.io/dashboard?plan=hobby。登录后立刻发放,无需绑卡,60 请求/分钟——用来读合约绰绰有余。

连注册都懒得注册?那就本地跑:npx -y @tonnode/mcp。完整的工具列表和参数说明见 tonnode.io/mcp

读取区块链状态,不该再意味着组装一个客户端。它该意味着——把你的问题说清楚。

让你的智能体接入 TON

16 个 MCP 工具:读取、非托管兑换、跨链与钱包。免费档 60 次/分钟,无需银行卡。