Skip to main content
Glama

decodeTx

Decode blockchain transactions into structured JSON with function names, parameters, event logs, and gas information for Ethereum and other EVM chains.

Instructions

트랜잭션을 구조화된 JSON으로 해석합니다 (함수명, 파라미터, 이벤트 로그, 가스 정보)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
txHashYes트랜잭션 해시 (0x...)
chainNo체인 (ethereum, polygon, arbitrum, base, optimism)ethereum

Implementation Reference

  • The main handler function that executes transaction decoding, including fetching transaction data, decoding function calls, and event logs.
    async function handler(args: z.infer<typeof inputSchema>): Promise<ToolResult<DecodeTxData>> {
      const { txHash, chain } = args;
    
      if (!isSupportedChain(chain)) {
        return makeError(`Unsupported chain: ${chain}`, "CHAIN_NOT_SUPPORTED");
      }
    
      if (!txHash.startsWith("0x") || txHash.length !== 66) {
        return makeError(`Invalid transaction hash: ${txHash}`, "INVALID_INPUT");
      }
    
      const cacheKey = `decodetx:${chain}:${txHash.toLowerCase()}`;
      const cached = cache.get<DecodeTxData>(cacheKey);
      if (cached.hit) return makeSuccess(chain as SupportedChain, cached.data, true);
    
      const client = getClient(chain as SupportedChain);
    
      try {
        const tx = await client.getTransaction({ hash: txHash as `0x${string}` });
    
        let status: "success" | "failed" | "pending" = "pending";
        let gasUsed: string | null = null;
        let gasPrice: string | null = null;
        let timestamp: number | null = null;
        let logs: Array<{ address: string; data: string; topics: string[] }> = [];
    
        if (tx.blockNumber) {
          try {
            const receipt = await client.getTransactionReceipt({ hash: txHash as `0x${string}` });
            status = receipt.status === "success" ? "success" : "failed";
            gasUsed = receipt.gasUsed.toString();
            gasPrice = formatGwei(receipt.effectiveGasPrice);
            logs = receipt.logs.map((l) => ({
              address: l.address,
              data: l.data,
              topics: l.topics as string[],
            }));
          } catch {
            status = "success";
          }
    
          try {
            const block = await client.getBlock({ blockNumber: tx.blockNumber });
            timestamp = Number(block.timestamp);
          } catch {
            // 무시
          }
        }
    
        // 함수 디코딩
        let decodedFunction: DecodedFunction | null = null;
        const inputData = tx.input;
    
        if (inputData && inputData !== "0x" && inputData.length >= 10) {
          const selector = inputData.slice(0, 10);
    
          // ABI가 있으면 정밀 디코딩
          if (tx.to) {
            const abiResult = await getABI(tx.to, chain as SupportedChain);
            if (abiResult) {
              try {
                const decoded = decodeFunctionData({
                  abi: abiResult.abi as Abi,
                  data: inputData as `0x${string}`,
                });
                decodedFunction = {
                  name: decoded.functionName,
                  signature: null,
                  // decoded.args는 readonly 배열일 수 있으므로 unknown으로 먼저 변환 후 직렬화
                  args: decoded.args ? Object.fromEntries(
                    Object.entries(decoded.args as unknown as Record<string, unknown>).map(([k, v]) => [k, serializeArg(v)])
                  ) : {},
                };
              } catch {
                // ABI 디코딩 실패 시 셀렉터 폴백
              }
            }
          }
    
          // ABI 디코딩 실패 시 셀렉터 기반 조회
          if (!decodedFunction) {
            const sigInfo = await resolveFunctionSelector(selector);
            if (sigInfo) {
              decodedFunction = {
                name: sigInfo.name,
                signature: sigInfo.signature,
                args: { _raw: inputData },
              };
            } else {
              decodedFunction = {
                name: `unknown(${selector})`,
                signature: null,
                args: { _raw: inputData },
              };
            }
          }
        }
    
        // 이벤트 로그 디코딩
        const decodedEvents: DecodedEvent[] = [];
        for (const log of logs) {
          // ABI가 있으면 디코딩 시도
          const logAbiResult = await getABI(log.address, chain as SupportedChain);
          if (logAbiResult) {
            try {
              const decoded = decodeEventLog({
                abi: logAbiResult.abi as Abi,
                data: log.data as `0x${string}`,
                topics: log.topics as [`0x${string}`, ...`0x${string}`[]],
              });
              decodedEvents.push({
                // eventName이 undefined일 수 있으므로 fallback 처리
                name: decoded.eventName ?? "unknown",
                address: log.address,
                // decoded.args는 readonly 배열일 수 있으므로 unknown으로 먼저 변환 후 직렬화
                args: decoded.args ? Object.fromEntries(
                  Object.entries(decoded.args as unknown as Record<string, unknown>).map(([k, v]) => [k, serializeArg(v)])
                ) : {},
              });
              continue;
            } catch {
              // 디코딩 실패 시 raw 로그
            }
          }
    
          decodedEvents.push({
            name: log.topics[0]?.slice(0, 10) ?? "unknown",
            address: log.address,
            args: { _topics: log.topics, _data: log.data },
          });
        }
    
        const data: DecodeTxData = {
          hash: tx.hash,
          from: tx.from,
          to: tx.to,
          value: formatEther(tx.value),
          status,
          function: decodedFunction,
          events: decodedEvents,
          gasUsed,
          gasPrice,
          blockNumber: tx.blockNumber ? Number(tx.blockNumber) : null,
          timestamp,
        };
    
        if (status !== "pending") {
          cache.set(cacheKey, data, DECODE_CACHE_TTL);
        }
    
        return makeSuccess(chain as SupportedChain, data, false);
      } catch (err) {
        const message = sanitizeError(err);
        if (message.includes("not found") || message.includes("could not be found")) {
          return makeError(`Transaction not found: ${txHash}`, "TX_NOT_FOUND");
        }
        return makeError(`Failed to decode transaction: ${message}`, "RPC_ERROR");
      }
    }
  • Input validation schema for the decodeTx tool.
    const inputSchema = z.object({
      txHash: z.string().describe("트랜잭션 해시 (0x...)"),
      chain: z.string().default("ethereum").describe("체인 (ethereum, polygon, arbitrum, base, optimism)"),
    });
  • Registration of the decodeTx tool in the MCP server.
    export function register(server: McpServer) {
      server.tool(
        "decodeTx",
        "트랜잭션을 구조화된 JSON으로 해석합니다 (함수명, 파라미터, 이벤트 로그, 가스 정보)",
        inputSchema.shape,
        async (args) => {
          const result = await handler(args as z.infer<typeof inputSchema>);
          return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
        },
      );
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It successfully discloses the output structure (function names, logs, gas data) which compensates for the missing output schema. However, it fails to disclose other behavioral traits like read-only safety, error handling for invalid hashes, or rate limiting.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single efficient sentence with a parenthetical clarifying the JSON contents. Every element earns its place: the verb defines the action, the format specifies the output structure, and the parenthetical lists the specific decoded components without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of output schema, the description appropriately compensates by detailing what the structured JSON contains (function name, parameters, logs, gas). For a 2-parameter decoding tool, this is sufficient, though it could be improved by noting read-only nature or supported chains.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% (both `txHash` and `chain` are well-documented in the schema). The description focuses entirely on the output behavior and adds no additional parameter semantics, meeting the baseline expectation for high-coverage schemas.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool decodes/parses (해석합니다) transactions into structured JSON, specifying exactly what data is extracted (function name, parameters, event logs, gas info). However, it does not explicitly differentiate from siblings like `getTxStatus` or `simulateTx`, which also interact with transactions but for different purposes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It does not indicate when to prefer this over `getTxStatus` (for confirmation status) or `simulateTx` (for simulating future transactions), nor does it mention prerequisites like having a valid transaction hash.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/calintzy/evmscope'

If you have feedback or need assistance with the MCP directory API, please join our Discord server