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
| Name | Required | Description | Default |
|---|---|---|---|
| amount | Yes | The amount to transfer (in base units, considering decimals) | |
| contractAddress | Yes | The contract address (e.g., SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9) | |
| contractName | Yes | The contract name of the SIP-010 token | |
| memo | No | Optional memo (max 34 bytes) | |
| network | Yes | The Stacks network | |
| recipient | Yes | The recipient's Stacks address | |
| sender | Yes | The 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}`; } },
- src/tools/index.ts:50-50 (registration)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}`; } }, };