Skip to main content
Glama

build_userop

Constructs unsigned ERC-4337 UserOperations from transaction requests for smart account wallets, enabling gas and paymaster field completion before signing.

Instructions

Build an unsigned ERC-4337 UserOperation from a transaction request. Returns sender, nonce, callData, buildId. Platform fills gas/paymaster fields before signing.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
wallet_idYesSmart Account wallet ID (UUID).
typeYesTransaction type.
toNoRecipient address.
amountNoAmount in smallest units (wei). Example: "1000000000000000000" = 1 ETH, "1000000" = 1 USDC
tokenNoToken contract address (for TOKEN_TRANSFER/APPROVE).
contractNoContract address (for CONTRACT_CALL).
methodNoContract method name (for CONTRACT_CALL).
abiNoContract ABI (for CONTRACT_CALL).
argsNoContract method args (for CONTRACT_CALL).
callsNoBatch calls (for BATCH type).
networkYesEVM network (e.g., "ethereum-sepolia").

Implementation Reference

  • The handler function for the `build_userop` tool which constructs a POST request to the `/v1/wallets/:id/userop/build` endpoint.
        async (args) => {
          const request: Record<string, unknown> = { type: args.type };
          if (args.to) request.to = args.to;
          if (args.amount) request.amount = args.amount;
          if (args.token) request.token = args.token;
          if (args.contract) request.contract = args.contract;
          if (args.method) request.method = args.method;
          if (args.abi) request.abi = args.abi;
          if (args.args) request.args = args.args;
          if (args.calls) request.calls = args.calls;
          const body = { request, network: args.network };
          const result = await apiClient.post(
            `/v1/wallets/${args.wallet_id}/userop/build`,
            body,
          );
          return toToolResult(result);
        },
      );
    }
  • Input schema validation for the `build_userop` tool using Zod.
    {
      wallet_id: z.string().describe('Smart Account wallet ID (UUID).'),
      type: z.enum(['TRANSFER', 'TOKEN_TRANSFER', 'CONTRACT_CALL', 'APPROVE', 'BATCH']).describe('Transaction type.'),
      to: z.string().optional().describe('Recipient address.'),
      amount: z.string().optional().describe('Amount in smallest units (wei). Example: "1000000000000000000" = 1 ETH, "1000000" = 1 USDC'),
      token: z.string().optional().describe('Token contract address (for TOKEN_TRANSFER/APPROVE).'),
      contract: z.string().optional().describe('Contract address (for CONTRACT_CALL).'),
      method: z.string().optional().describe('Contract method name (for CONTRACT_CALL).'),
      abi: z.array(z.unknown()).optional().describe('Contract ABI (for CONTRACT_CALL).'),
      args: z.array(z.unknown()).optional().describe('Contract method args (for CONTRACT_CALL).'),
      calls: z.array(z.object({ to: z.string(), amount: z.string().optional().describe('Amount in smallest units (wei)'), data: z.string().optional() })).optional().describe('Batch calls (for BATCH type).'),
      network: z.string().describe('EVM network (e.g., "ethereum-sepolia").'),
    },
  • Registration function `registerBuildUserop` for the `build_userop` tool.
    export function registerBuildUserop(
      server: McpServer,
      apiClient: ApiClient,
      walletContext?: WalletContext,
    ): void {
      server.tool(
        'build_userop',
        withWalletPrefix(
          'Build an unsigned ERC-4337 UserOperation from a transaction request. Returns sender, nonce, callData, buildId. Platform fills gas/paymaster fields before signing.',
          walletContext?.walletName,
        ),
        {
          wallet_id: z.string().describe('Smart Account wallet ID (UUID).'),
          type: z.enum(['TRANSFER', 'TOKEN_TRANSFER', 'CONTRACT_CALL', 'APPROVE', 'BATCH']).describe('Transaction type.'),
          to: z.string().optional().describe('Recipient address.'),
          amount: z.string().optional().describe('Amount in smallest units (wei). Example: "1000000000000000000" = 1 ETH, "1000000" = 1 USDC'),
          token: z.string().optional().describe('Token contract address (for TOKEN_TRANSFER/APPROVE).'),
          contract: z.string().optional().describe('Contract address (for CONTRACT_CALL).'),
          method: z.string().optional().describe('Contract method name (for CONTRACT_CALL).'),
          abi: z.array(z.unknown()).optional().describe('Contract ABI (for CONTRACT_CALL).'),
          args: z.array(z.unknown()).optional().describe('Contract method args (for CONTRACT_CALL).'),
          calls: z.array(z.object({ to: z.string(), amount: z.string().optional().describe('Amount in smallest units (wei)'), data: z.string().optional() })).optional().describe('Batch calls (for BATCH type).'),
          network: z.string().describe('EVM network (e.g., "ethereum-sepolia").'),
        },
        async (args) => {
          const request: Record<string, unknown> = { type: args.type };
          if (args.to) request.to = args.to;
          if (args.amount) request.amount = args.amount;
          if (args.token) request.token = args.token;
          if (args.contract) request.contract = args.contract;
          if (args.method) request.method = args.method;
          if (args.abi) request.abi = args.abi;
          if (args.args) request.args = args.args;
          if (args.calls) request.calls = args.calls;
          const body = { request, network: args.network };
          const result = await apiClient.post(
            `/v1/wallets/${args.wallet_id}/userop/build`,
            body,
          );
          return toToolResult(result);
        },
      );
    }
Behavior3/5

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

With no annotations provided, the description carries the disclosure burden. It successfully explains what fields are populated (returns) versus what the platform handles later (gas/paymaster), clarifying the partial state of the output. However, it omits side effects (e.g., whether buildId is persisted), error conditions, or idempotency characteristics.

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?

Two sentences with zero waste: the first states purpose and return values, the second clarifies platform-managed fields. Information is front-loaded, appropriately dense for the complexity level, and avoids repetition of schema details.

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?

Despite having no output schema, the description lists the specific return fields, satisfying that gap. For a complex ERC-4337 operation with conditional parameter sets (based on transaction type), it provides sufficient completeness, though mentioning the buildId's purpose (likely for sign_userop) would strengthen it further.

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%, providing detailed documentation for all 11 parameters including examples (e.g., amount in wei). The description references a 'transaction request' but adds no semantic clarification beyond the schema's detailed field descriptions, warranting the baseline score of 3 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 specifies it 'Build[s] an unsigned ERC-4337 UserOperation' with specific return values (sender, nonce, callData, buildId), giving a concrete verb and resource. It identifies the domain standard (ERC-4337) which distinguishes it from regular transaction tools like send_token, though it doesn't explicitly differentiate from the sibling sign_userop.

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

Usage Guidelines3/5

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

The description hints at workflow sequence by stating 'Platform fills gas/paymaster fields before signing,' implying this precedes a signing step. However, it lacks explicit guidance on when to use this low-level builder versus high-level alternatives like send_token or call_contract, or whether this is strictly a prerequisite for sign_userop.

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/minhoyoo-iotrust/WAIaaS'

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