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
| Name | Required | Description | Default |
|---|---|---|---|
| wallet_id | Yes | Smart Account wallet ID (UUID). | |
| type | Yes | Transaction type. | |
| to | No | Recipient address. | |
| amount | No | Amount in smallest units (wei). Example: "1000000000000000000" = 1 ETH, "1000000" = 1 USDC | |
| token | No | Token contract address (for TOKEN_TRANSFER/APPROVE). | |
| contract | No | Contract address (for CONTRACT_CALL). | |
| method | No | Contract method name (for CONTRACT_CALL). | |
| abi | No | Contract ABI (for CONTRACT_CALL). | |
| args | No | Contract method args (for CONTRACT_CALL). | |
| calls | No | Batch calls (for BATCH type). | |
| network | Yes | EVM 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").'), }, - packages/mcp/src/tools/build-userop.ts:14-56 (registration)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); }, ); }