Skip to main content
Glama

generate_sip010_transfer

Create SIP-010 token transfer transactions with required post-conditions for secure blockchain transfers. Generates transaction parameters ready for wallet signing on Stacks networks.

Instructions

Generate a SIP-010 fungible token transfer transaction with proper post-conditions. Returns the transaction parameters for wallet signing.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
amountYesThe amount to transfer (in base units, considering decimals)
contractAddressYesThe contract address (e.g., SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9)
contractNameYesThe contract name of the SIP-010 token
memoNoOptional memo (max 34 bytes)
networkYesThe Stacks network
recipientYesThe recipient's Stacks address
senderYesThe sender's Stacks address

Implementation Reference

  • Zod schema defining the input parameters for the generate_sip010_transfer tool, including contract details, amount, addresses, optional memo, and network.
    const SIP010TransferScheme = z.object({
      contractAddress: z.string().describe("The contract address (e.g., SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9)"),
      contractName: z.string().describe("The contract name of the SIP-010 token"),
      amount: z.number().describe("The amount to transfer (in base units, considering decimals)"),
      sender: z.string().describe("The sender's Stacks address"),
      recipient: z.string().describe("The recipient's Stacks address"),
      memo: z.string().optional().describe("Optional memo (max 34 bytes)"),
      network: NetworkScheme.describe("The Stacks network"),
    });
  • The main execute function of the tool that validates inputs, generates transaction parameters for SIP-010 transfer including function args and post-conditions, and returns formatted JSON and integration code.
      execute: async (args, context) => {
        try {
          await recordTelemetry({ action: "generate_sip010_transfer" }, context);
          
          // Validate inputs
          if (args.amount <= 0) {
            return "❌ Amount must be positive";
          }
          
          if (args.sender === args.recipient) {
            return "❌ Sender and recipient cannot be the same";
          }
          
          if (args.memo && Buffer.from(args.memo, 'utf8').length > 34) {
            return "❌ Memo cannot exceed 34 bytes";
          }
          
          // Generate the transaction parameters
          const transactionParams = {
            contractAddress: args.contractAddress,
            contractName: args.contractName,
            functionName: "transfer",
            functionArgs: [
              { type: "uint", value: args.amount },
              { type: "principal", value: args.sender },
              { type: "principal", value: args.recipient },
              args.memo ? { type: "some", value: { type: "buffer", value: args.memo } } : { type: "none" }
            ],
            postConditions: [
              {
                type: "fungible",
                condition: "equal",
                amount: args.amount,
                asset: {
                  contractAddress: args.contractAddress,
                  contractName: args.contractName,
                  assetName: args.contractName
                },
                principal: args.sender
              }
            ],
            network: args.network
          };
          
          return `# SIP-010 Transfer Transaction
    
    ## Transaction Parameters
    \`\`\`json
    ${JSON.stringify(transactionParams, null, 2)}
    \`\`\`
    
    ## Frontend Integration Example (TypeScript)
    
    \`\`\`typescript
    import { 
      openContractCall,
      PostConditionMode,
      FungibleConditionCode,
      createAssetInfo,
      makeStandardFungiblePostCondition,
      uintCV,
      principalCV,
      ${args.memo ? 'someCV, bufferCV' : 'noneCV'}
    } from '@stacks/connect';
    
    const functionArgs = [
      uintCV(${args.amount}),
      principalCV('${args.sender}'),
      principalCV('${args.recipient}'),
      ${args.memo ? `someCV(bufferCV(Buffer.from('${args.memo}', 'utf8')))` : 'noneCV()'}
    ];
    
    const postConditions = [
      makeStandardFungiblePostCondition(
        '${args.sender}',
        FungibleConditionCode.Equal,
        ${args.amount},
        createAssetInfo('${args.contractAddress}', '${args.contractName}', '${args.contractName}')
      ),
    ];
    
    await openContractCall({
      contractAddress: '${args.contractAddress}',
      contractName: '${args.contractName}',
      functionName: 'transfer',
      functionArgs,
      postConditions,
      postConditionMode: PostConditionMode.Deny, // REQUIRED
      onFinish: (data) => {
        console.log('Transaction ID:', data.txId);
      },
    });
    \`\`\`
    
    ## Security Notes
    - ✅ Post-condition included (MANDATORY for SIP-010)
    - ✅ Input validation performed
    - ✅ Using deny mode for maximum security
    - ⚠️  Always verify recipient address before signing`;
          
        } catch (error) {
          return `❌ Failed to generate SIP-010 transfer: ${error}`;
        }
      },
  • Registration of the generateSIP010TransferTool with the FastMCP server.
    server.addTool(generateSIP010TransferTool);
  • Full tool definition including name, description, parameters schema reference, and execute handler.
    export const generateSIP010TransferTool: Tool<undefined, typeof SIP010TransferScheme> = {
      name: "generate_sip010_transfer",
      description: "Generate a SIP-010 fungible token transfer transaction with proper post-conditions. Returns the transaction parameters for wallet signing.",
      parameters: SIP010TransferScheme,
      execute: async (args, context) => {
        try {
          await recordTelemetry({ action: "generate_sip010_transfer" }, context);
          
          // Validate inputs
          if (args.amount <= 0) {
            return "❌ Amount must be positive";
          }
          
          if (args.sender === args.recipient) {
            return "❌ Sender and recipient cannot be the same";
          }
          
          if (args.memo && Buffer.from(args.memo, 'utf8').length > 34) {
            return "❌ Memo cannot exceed 34 bytes";
          }
          
          // Generate the transaction parameters
          const transactionParams = {
            contractAddress: args.contractAddress,
            contractName: args.contractName,
            functionName: "transfer",
            functionArgs: [
              { type: "uint", value: args.amount },
              { type: "principal", value: args.sender },
              { type: "principal", value: args.recipient },
              args.memo ? { type: "some", value: { type: "buffer", value: args.memo } } : { type: "none" }
            ],
            postConditions: [
              {
                type: "fungible",
                condition: "equal",
                amount: args.amount,
                asset: {
                  contractAddress: args.contractAddress,
                  contractName: args.contractName,
                  assetName: args.contractName
                },
                principal: args.sender
              }
            ],
            network: args.network
          };
          
          return `# SIP-010 Transfer Transaction
    
    ## Transaction Parameters
    \`\`\`json
    ${JSON.stringify(transactionParams, null, 2)}
    \`\`\`
    
    ## Frontend Integration Example (TypeScript)
    
    \`\`\`typescript
    import { 
      openContractCall,
      PostConditionMode,
      FungibleConditionCode,
      createAssetInfo,
      makeStandardFungiblePostCondition,
      uintCV,
      principalCV,
      ${args.memo ? 'someCV, bufferCV' : 'noneCV'}
    } from '@stacks/connect';
    
    const functionArgs = [
      uintCV(${args.amount}),
      principalCV('${args.sender}'),
      principalCV('${args.recipient}'),
      ${args.memo ? `someCV(bufferCV(Buffer.from('${args.memo}', 'utf8')))` : 'noneCV()'}
    ];
    
    const postConditions = [
      makeStandardFungiblePostCondition(
        '${args.sender}',
        FungibleConditionCode.Equal,
        ${args.amount},
        createAssetInfo('${args.contractAddress}', '${args.contractName}', '${args.contractName}')
      ),
    ];
    
    await openContractCall({
      contractAddress: '${args.contractAddress}',
      contractName: '${args.contractName}',
      functionName: 'transfer',
      functionArgs,
      postConditions,
      postConditionMode: PostConditionMode.Deny, // REQUIRED
      onFinish: (data) => {
        console.log('Transaction ID:', data.txId);
      },
    });
    \`\`\`
    
    ## Security Notes
    - ✅ Post-condition included (MANDATORY for SIP-010)
    - ✅ Input validation performed
    - ✅ Using deny mode for maximum security
    - ⚠️  Always verify recipient address before signing`;
          
        } catch (error) {
          return `❌ Failed to generate SIP-010 transfer: ${error}`;
        }
      },
    };
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions 'proper post-conditions' and 'returns the transaction parameters for wallet signing,' which gives some context about output and safety (non-destructive generation). However, it lacks details on error conditions, rate limits, authentication needs, or what constitutes 'proper' post-conditions. For a transaction generation tool with zero annotation coverage, this is insufficient.

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 highly concise and well-structured in two sentences: the first states the purpose and key constraint, and the second specifies the return value. Every word earns its place, with no redundancy or fluff, making it easy to parse quickly.

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

Completeness3/5

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

Given the complexity (transaction generation with 7 parameters), no annotations, and no output schema, the description is minimally adequate but has gaps. It covers the basic purpose and output format but lacks behavioral details (e.g., error handling, side effects) and usage context. With 100% schema coverage, the parameter documentation is handled, but overall completeness is limited for a tool that generates financial transactions.

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?

The description adds no parameter-specific information beyond what's already in the input schema, which has 100% coverage with detailed descriptions for all 7 parameters. The baseline is 3 when schema coverage is high, as the schema adequately documents parameters like 'amount' (in base units), 'contractAddress' (format example), and 'network' (enum values). No additional semantic value is provided in the description.

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's purpose: 'Generate a SIP-010 fungible token transfer transaction with proper post-conditions.' It specifies the verb ('generate'), resource ('SIP-010 fungible token transfer transaction'), and key constraint ('with proper post-conditions'). However, it doesn't explicitly differentiate from sibling tools like 'generate_sip009_transfer' or 'generate_fungible_post_condition', which would require a 5.

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 doesn't mention sibling tools like 'generate_sip009_transfer' for different token standards, 'generate_fungible_post_condition' for just post-conditions, or 'generate_stx_post_condition' for STX transfers. There's no context about prerequisites, such as needing token balances or network connectivity.

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/exponentlabshq/stacks-clarity-mcp'

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