TON 合约 get 方法怎么调?run_get_method 一次搞定,不用 SDK
用 run_get_method 调用 TON 合约的任意 get 方法:seqno、get_jetton_data、get_sale_data,参数与栈怎么传、exit_code 怎么读、返回栈怎么解析——全程不用 SDK。
你打开区块浏览器,只是想在发交易前查一下钱包的 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(ton、tonweb、tonutils)、自己写一个连到 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。
输出以一个值栈返回:int、slice、cell、tuple。智能体按位置解析它——第一个值、第二个、第三个。比如 get_jetton_data 依次返回:total_supply(int)、mintable 标志、admin(slice 地址)、content(cell)以及钱包代码(cell)。位置决定含义——这是合约自身 ABI 约定的一部分。
很多常用方法(seqno、get_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_supply、mintable 标志、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_method、get_jetton_info、parse_address、get_account_state——就像普通工具一样暴露给智能体。调用起来就是聊天里的一句话:“对这个 DeDust 池子调用 get_pool_data”、“读一下这个钱包的 seqno”——智能体会自己选中 run_get_method、组装参数,并把字段解释清楚。而如果你的任务是查 USDT 余额,还有一条现成的单命令路径:一次调用查出 TON 上的 USDT 余额。
领一个免费的 Hobby key,直接从智能体里调用 run_get_method → tonnode.io/dashboard?plan=hobby。登录后立刻发放,无需绑卡,60 请求/分钟——用来读合约绰绰有余。
连注册都懒得注册?那就本地跑:npx -y @tonnode/mcp。完整的工具列表和参数说明见 tonnode.io/mcp。
读取区块链状态,不该再意味着组装一个客户端。它该意味着——把你的问题说清楚。