전체 글 보기
6 분 소요

TON get 메서드를 SDK 없이 호출하는 방법 (run_get_method)

run_get_method로 TON 컨트랙트의 모든 get 메서드를 SDK 없이 호출하기 — seqno, get_jetton_data, get_sale_data, 인자 전달, exit_code, 스택 파싱까지

run_get_methodTON get 메서드TVM exit_codeSDK 없는 TONTON MCPget_jetton_data

트랜잭션을 보내기 전에 지갑의 seqno만 확인하려고 익스플로러를 열었습니다. 아니면 Jetton의 total_supply가 필요했거나, 마켓플레이스의 NFT 리스팅 데이터가 필요했을 수도 있습니다. 그런데 또다시 npm i @ton/ton을 치고, TonClient를 띄우고, 살아 있는 라이트서버 엔드포인트를 찾아 헤매고, 주소를 beginCell().storeAddress()로 어떻게 포장하는지 알아내고, 마지막엔 출력 BOC까지 손으로 파싱하고 있습니다. 서른 줄짜리 코드 — 컨트랙트가 어차피 공짜로 내주는 숫자 하나를 얻자고 말입니다.

문제는 TON이 아닙니다. 문제는 여러분과 단순한 read-only 호출 사이에 SDK라는 계층이 통째로 놓여 있다는 것입니다. 설치해야 하고, 설정해야 하고, 다음 업데이트에서 깨지지 않게 지켜야 하는 계층입니다. 게다가 TON에 AI 에이전트(Claude, Cursor, ChatGPT/Codex)를 붙이는 상황이라면 이 계층을 직접 작성하는 것은 아예 말이 되지 않습니다. 에이전트는 그냥 메서드를 호출하면 됩니다.

아래에서는 SDK 클라이언트 코드 한 줄 없이 run_get_method로 TON 컨트랙트의 모든 get 메서드를 호출하는 방법을 정리합니다.

TON 컨트랙트의 get 메서드란 무엇이고, 왜 보통 SDK를 끌고 오는가

get 메서드는 스마트 컨트랙트의 read-only 함수입니다. 지갑은 seqno를, Jetton 마스터 컨트랙트는 get_jetton_data를, NFT 판매 컨트랙트는 get_sale_data를, STON.fi/DeDust 풀은 get_pool_data를 내줍니다. 핵심 단어는 read-only입니다:

  • 이 메서드는 컨트랙트의 상태를 아무것도 바꾸지 않습니다.
  • 서명이 필요 없고, 사용자의 가스를 쓰지 않습니다.
  • 트랜잭션 전송이 아니라 라이트서버 또는 TVM(TON Virtual Machine) 에뮬레이터에서 실행됩니다.

즉 get 메서드 호출은 트랜잭션이 아닙니다. 서명할 것도, 지불할 것도, 블록에서 기다릴 것도 없습니다. 본질적으로 "살아 있는 컨트랙트에서 값을 읽어라"라는 함수입니다.

그런데 이걸 "예전 방식"으로 호출하려면 부속품이 한 뭉치 필요합니다. SDK(ton, tonweb, tonutils), 라이트서버까지 직접 연결하는 ADNL 클라이언트 또는 toncenter 같은 HTTP 래퍼, 입력 인자를 셀로 손수 조립하는 작업, 그리고 출력 BOC를 손으로 파싱하는 작업까지. 읽기 한 번에 움직이는 부품이 너무 많습니다. 그리고 부품 하나하나가 전부 장애 지점입니다. 글로벌 설정에 들어 있는 공개 라이트서버는 모두가 공유하며 한도가 걸려 있어서 부하가 몰리면 not ready나 ADNL 타임아웃을 돌려주고, 공개 HTTP API는 한도를 넘기면 429 Too Many Requests를 반환합니다(키 없이는 대략 초당 한 요청 수준입니다).

run_get_method: 모든 컨트랙트의 모든 read-only 메서드를 위한 도구 하나

run_get_method모든 TON 컨트랙트의 모든 read-only get 메서드를 호출하는 MCP 도구입니다. 컨트랙트 주소, 메서드 이름, 인자 목록을 넘기면 도구가 해당 메서드를 TVM에서 온체인으로 실행하고 출력 스택을 돌려줍니다.

이때 여러분에게 필요 없는 것들:

  • SDK(ton/tonweb/tonutils) 설치와 업데이트
  • 직접 만드는 ADNL 클라이언트
  • 인자를 셀로 수동 포장하고 출력 BOC를 수동 파싱하는 작업

run_get_method는 TON용 hosted MCP 서버인 TONNode의 읽기 도구 세트에 포함되어 있습니다. MCP(Model Context Protocol)는 AI 에이전트가 도구를 호출하는 방식을 정한 표준입니다. 읽기 세트 전체는 npx -y @tonnode/mcp로컬에서 무료로 쓸 수 있고(공개 설정, 카드 불필요), 또는 Bearer 키를 쓰는 hosted 엔드포인트 https://mcp.tonnode.io/mcp로도 쓸 수 있습니다. 아무것도 결제하지 않고 지금 당장 시험해볼 수 있습니다.

인자와 스택: 입력을 넘기고 TVM 출력을 해석하는 법

TVM은 스택으로 동작합니다. 비유하자면, 입력값이 적힌 "카드"를 몇 장 테이블에 올려두면 메서드가 그걸 가져가 처리하고, 결과가 적힌 "카드"를 다시 올려놓는 식입니다.

입력은 TVM 스택의 값으로 전달됩니다. 보통 다음과 같습니다:

  • int — 정수(예: 인덱스, raw 단위 금액, query id)
  • 주소 slice — 셀의 슬라이스로 포장된 주소
  • cell — 임의의 데이터 셀

출력은 값들의 출력 스택으로 돌아옵니다: int, slice, cell, tuple. 에이전트는 이를 위치 기준으로 해석합니다 — 첫 번째 값, 두 번째 값, 세 번째 값. 예를 들어 get_jetton_data는 순서대로 total_supply(int), mintable 플래그, admin(slice 주소), content(cell), 그리고 지갑 코드(cell)를 돌려줍니다. 의미를 결정하는 것은 위치이며, 이는 컨트랙트 자체의 ABI 규약에 속합니다.

쓸모 있는 메서드 상당수(seqno, get_jetton_data)는 입력을 아예 요구하지 않습니다 — 인자 스택이 비어 있습니다. 인자는 메서드가 키로 무언가를 찾아야 할 때 등장합니다. 예를 들어 소유자 주소로부터 Jetton 지갑 주소를 계산하는 경우입니다.

여기서 한 가지 중요한 점이 있습니다. run_get_methodraw 단위의 원시 스택을 돌려줍니다. 숫자는 decimals로 환산되지 않은 채 그대로 오고, 셀도 읽기 좋은 문자열로 변환되지 않습니다. 유연하지만 지금 무엇을 읽고 있는지 이해하고 있어야 합니다 — 이 이야기는 get_jetton_info 섹션에서 다시 다루겠습니다.

exit_code: 메서드가 제대로 돌았는지 확인하는 법

모든 get 메서드 호출에는 TVM 종료 코드인 exit_code가 붙습니다. 출력 스택을 해석하는 건 호출 자체가 성공했을 때만 의미가 있습니다. get 메서드의 규칙은 직관에 어긋나므로 외워두는 편이 좋습니다:

  • exit_code 0과 1은 성공입니다(둘 다 정상 종료로 취급되니 1이 나와도 놀라지 마십시오).
  • exit_code > 1은 오류이며, 이때 출력 스택은 신뢰할 수 없습니다.

자주 만나는 오류 코드:

exit_code 의미
2 stack underflow — 메서드가 기대하는 것보다 적은 인자를 스택에 올렸습니다
4 integer overflow / 0으로 나누기
11 대개 존재하지 않는 메서드 호출(이름 오타)
13 out of gas

실전에서는 이렇습니다. 2가 나왔다면 인자를 전부, 그리고 올바른 순서로 넘겼는지 확인하십시오. 11이 나왔다면 메서드 이름에 오타가 있거나 컨트랙트가 그 메서드를 구현하지 않았을 가능성이 큽니다. 13이 나왔다면 메서드가 무거워서 가스 한도에 부딪힌 것입니다.

에이전트를 통한 예시: 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 플래그, 관리자 주소, 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_datatotal_supply를 raw 단위의 거대한 정수로 던져주고, content는 이름과 심볼을 다시 꺼내야 하는 셀로 줍니다. "USDT"도, "6 decimals"도, 예쁘게 정리된 숫자도 없습니다 — 저수준 TVM 출력이니까요. 마스터 컨트랙트에 대한 저수준 접근이나 비표준 필드가 정말로 필요하다면 바로 이 도구가 정답입니다.

하지만 목적이 그저 Jetton의 이름, 심볼, decimals를 완성된 형태로 알아내는 것이라면 셀 파싱으로 고생하지 마십시오. 그럴 때를 위해 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_addressEQ/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)에 있으며, HTTP 중간 계층 없이 TON의 네이티브 ADNL 프로토콜로 동작합니다. 무료로 쓰는 방법은 TON용 MCP 무료로 쓰기에서 따로 다뤘습니다.

둘째는 보장된 처리량과 전용 키를 제공하는 hosted 엔드포인트입니다:

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

모든 요금제에서 16개 도구를 전부 쓸 수 있습니다 — 비용은 처리량에 대해서만 지불합니다. 무료 Hobby 키(60 요청/분)는 로그인 직후 카드 없이 발급됩니다.

연결하고 나면 run_get_method, get_jetton_info, parse_address, get_account_state를 포함한 읽기 세트 전체가 에이전트에게 평범한 도구로 노출됩니다. 호출은 채팅의 평범한 문장처럼 보입니다. "이 DeDust 풀에서 get_pool_data를 호출해줘", "이 지갑의 seqno를 읽어줘" — 그러면 에이전트가 알아서 run_get_method와 인자를 고르고 필드를 설명해줍니다. 그리고 목적이 USDT 잔액 확인이라면 명령 한 번으로 끝나는 길이 따로 있습니다: TON의 USDT 잔액을 호출 한 번으로.


무료 Hobby 키를 받아서 에이전트에서 바로 run_get_method를 호출해보십시오tonnode.io/dashboard?plan=hobby. 키는 로그인 직후 카드 없이 발급되며, 분당 60 요청은 컨트랙트를 읽기에 넉넉하고도 남습니다.

가입조차 하기 싫다면 로컬에서 실행하십시오: npx -y @tonnode/mcp. 전체 도구 목록과 파라미터는 tonnode.io/mcp 페이지에 있습니다.

블록체인 상태를 읽는다는 것은 더 이상 클라이언트를 조립한다는 뜻이 아닙니다. 요청을 문장으로 표현한다는 뜻입니다.

에이전트에게 TON 접근 권한을 주세요

16가지 MCP 도구: 읽기, 논커스터디얼 스왑, 크로스체인, 지갑. 무료 요금제 60 요청/분, 카드 불필요.