I created some NFT tokens through OpenSea on the Polygon Mumbai testnet. Now I need to move these tokens to different wallet addresses using Alchemy’s web3 library. My setup involves a Node.js REST API without browser wallet integration, so I handle transaction signing programmatically.
You’re missing how NFT transfers actually work. NFTs are smart contracts, not regular tokens like ETH - you can’t just send them with basic transactions. Your transaction is missing the to field (contract address) and data field (encoded function call). I hit this same wall when I started doing programmatic NFT transfers. You need to call the actual ERC-721 contract for your collection. Grab the contract address from your NFT’s OpenSea page, then build a proper contract call using transferFrom or safeTransferFrom. That 53k gas limit won’t cut it either - NFT transfers need 80k-120k gas depending on the contract. Also double-check you own the token and have transfer approval if you’re calling from a different address.
you’re missing the contract interaction part completely. nft transfers aren’t like regular eth sends - you need to call the smart contract functions. grab the contract address from opensea (it’s in the item details) and use web3Instance.eth.Contract with the right abi. also, that gas limit’s def too low for nft transfers. try around 100k-150k.
I see what’s happening - you’re using a regular ETH transaction structure, but NFTs need contract method calls instead. Have you tried using the actual contract ABI for the transfer?
What NFT standard are you working with? ERC-721 or ERC-1155? The transfer method’s different for each. ERC-721 uses safeTransferFrom(from, to, tokenId) and ERC-1155 uses safeTransferFrom(from, to, id, amount, data).
Skip that custom txData object and try this instead:
Then use that encoded data as your data field and set the contract address as to.
Do you have your NFT collection’s contract address? Check OpenSea’s details section if you need it. Also, what error messages are you getting? Does it fail silently or throw something specific?
That 53000 gas limit might be too low depending on contract complexity. Try running estimateGas() first to see what you actually need.