transfer_nft
Execute NFT transfers between wallet addresses on EVM-compatible networks. Utilize the owner's private key to sign and send transactions for ERC721 tokens securely.
Instructions
Transfer an NFT (ERC721 token) from one address to another. Requires the private key of the current owner for signing the transaction.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| network | No | Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default. | |
| privateKey | Yes | Private key of the NFT owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored. | |
| toAddress | Yes | The recipient wallet address that will receive the NFT | |
| tokenAddress | Yes | The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club) | |
| tokenId | Yes | The ID of the specific NFT to transfer (e.g., '1234') |
Implementation Reference
- src/core/tools.ts:815-897 (registration)Registration of the 'transfer_nft' MCP tool, including input schema validation with Zod and the handler function that processes the request and delegates to services.transferERC721 for execution.// Transfer NFT (ERC721) server.tool( 'transfer_nft', 'Transfer an NFT (ERC721 token) from one address to another. Requires the private key of the current owner for signing the transaction.', { privateKey: z .string() .describe( 'Private key of the NFT owner account in hex format (with or without 0x prefix). SECURITY: This is used only for transaction signing and is not stored.' ), tokenAddress: z .string() .describe( "The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' for Bored Ape Yacht Club)" ), tokenId: z .string() .describe("The ID of the specific NFT to transfer (e.g., '1234')"), toAddress: z .string() .describe('The recipient wallet address that will receive the NFT'), network: z .string() .optional() .describe( "Network name (e.g., 'ethereum', 'optimism', 'arbitrum', 'base', 'polygon') or chain ID. Most NFTs are on Ethereum mainnet, which is the default." ) }, async ({ privateKey, tokenAddress, tokenId, toAddress, network = 'ethereum' }) => { try { // Get the formattedKey with 0x prefix const formattedKey = privateKey.startsWith('0x') ? (privateKey as `0x${string}`) : (`0x${privateKey}` as `0x${string}`); const result = await services.transferERC721( tokenAddress as Address, toAddress as Address, BigInt(tokenId), formattedKey, network ); return { content: [ { type: 'text', text: JSON.stringify( { success: true, txHash: result.txHash, network, collection: tokenAddress, tokenId: result.tokenId, recipient: toAddress, name: result.token.name, symbol: result.token.symbol }, null, 2 ) } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error transferring NFT: ${error instanceof Error ? error.message : String(error)}` } ], isError: true }; } } );
- src/core/services/transfer.ts:323-393 (handler)Core handler function transferERC721 that executes the NFT transfer using viem's writeContract to call transferFrom on the ERC721 contract, resolves ENS names, formats private key, fetches metadata, and returns transaction details.export async function transferERC721( tokenAddressOrEns: string, toAddressOrEns: string, tokenId: bigint, privateKey: string | `0x${string}`, network: string = 'ethereum' ): Promise<{ txHash: Hash; tokenId: string; token: { name: string; symbol: string; }; }> { // Resolve ENS names to addresses if needed const tokenAddress = (await resolveAddress( tokenAddressOrEns, network )) as Address; const toAddress = (await resolveAddress(toAddressOrEns, network)) as Address; // Ensure the private key has 0x prefix const formattedKey = typeof privateKey === 'string' && !privateKey.startsWith('0x') ? (`0x${privateKey}` as `0x${string}`) : (privateKey as `0x${string}`); // Create wallet client for sending the transaction const walletClient = getWalletClient(formattedKey, network); const fromAddress = walletClient.account!.address; // Send the transaction const hash = await walletClient.writeContract({ address: tokenAddress, abi: erc721TransferAbi, functionName: 'transferFrom', args: [fromAddress, toAddress, tokenId], account: walletClient.account!, chain: walletClient.chain }); // Get token metadata const publicClient = getPublicClient(network); const contract = getContract({ address: tokenAddress, abi: erc721TransferAbi, client: publicClient }); // Get token name and symbol let name = 'Unknown'; let symbol = 'NFT'; try { [name, symbol] = await Promise.all([ contract.read.name(), contract.read.symbol() ]); } catch (error) { console.error('Error fetching NFT metadata:', error); } return { txHash: hash, tokenId: tokenId.toString(), token: { name, symbol } }; }
- src/core/services/transfer.ts:54-88 (helper)ERC721 ABI definition used for NFT transfer operations, including transferFrom, name, symbol, and ownerOf functions.// Standard ERC721 ABI for transfers const erc721TransferAbi = [ { inputs: [ { type: 'address', name: 'from' }, { type: 'address', name: 'to' }, { type: 'uint256', name: 'tokenId' } ], name: 'transferFrom', outputs: [], stateMutability: 'nonpayable', type: 'function' }, { inputs: [], name: 'name', outputs: [{ type: 'string' }], stateMutability: 'view', type: 'function' }, { inputs: [], name: 'symbol', outputs: [{ type: 'string' }], stateMutability: 'view', type: 'function' }, { inputs: [{ type: 'uint256', name: 'tokenId' }], name: 'ownerOf', outputs: [{ type: 'address' }], stateMutability: 'view', type: 'function' } ] as const;
- src/server/server.ts:17-18 (registration)Top-level server initialization that calls registerEVMTools(server), which includes the transfer_nft tool registration.registerEVMResources(server); registerEVMTools(server);
- src/core/services/index.ts:4-5 (helper)Services index re-exporting transfer functions including transferERC721 for use in tools.ts.export * from './transfer.js'; export * from './blocks.js';