I’m working on an NFT project using OpenZeppelin’s ERC721Full contract. Minting works fine, but I’m stuck on creating a purchase feature. Here’s what I’ve tried:
function purchaseToken(uint256 tokenId) public payable {
address payable currentOwner = address(uint160(ownerOf(tokenId)));
_approve(currentOwner, tokenId);
setApprovalForAll(msg.sender, true);
safeTransferFrom(currentOwner, msg.sender, tokenId);
currentOwner.transfer(msg.value);
emit TokenSold(currentOwner, msg.sender, msg.value);
}
This doesn’t work because only the owner or an approved address can call approve. I’m not sure how to structure this correctly. Should I change the approve function? That feels wrong.
What’s the right way to set up a buy function for NFTs? I’ll add checks later, but for now I just want it to work in testing. Thanks for any help!
hey there sparklinggem! ur nft purchasing setup is pretty close, but I think we can make it smoother
have u considered using a marketplace contract? it could handle the buying and selling process for ya
instead of doing everything in one function, maybe try something like this:
contract NFTMarketplace {
// store NFT prices
mapping(uint256 => uint256) public tokenPrices;
function listNFT(uint256 tokenId, uint256 price) external {
// list NFT logic here
}
function buyNFT(uint256 tokenId) external payable {
// buying logic here
}
}
this way, the marketplace contract acts as a middleman. it keeps things organized and safer for everyone involved. what do u think about this approach? have u tried anything similar before?
also, just curious - what kind of NFTs are u working on? sounds like an exciting project! 

hey, ur close but not quite there. instead of trying to do everything in one function, split it up. have the owner list the NFT for sale first, then make a separate buy function. That way, the contract can handle the transfer nd payment smoothly. here’s a quick example:
function listForSale(uint256 tokenId, uint256 price) public {
// owner lists + approves
}
function buyToken(uint256 tokenId) public payable {
// buyer pays, contract transfers
}
hope that helps!
Your approach is close, but there are a few key issues to address. First, the purchase function should be separate from the transfer logic. Instead, create a ‘listForSale’ function where the owner sets a price and approves the contract to transfer. Then, implement a ‘buyToken’ function that handles the payment and transfer.
Here’s a simplified example:
mapping(uint256 => uint256) public tokenPrices;
function listForSale(uint256 tokenId, uint256 price) public {
require(ownerOf(tokenId) == msg.sender, 'Not the owner');
tokenPrices[tokenId] = price;
approve(address(this), tokenId);
}
function buyToken(uint256 tokenId) public payable {
require(tokenPrices[tokenId] > 0, 'Not for sale');
require(msg.value >= tokenPrices[tokenId], 'Insufficient payment');
address payable seller = payable(ownerOf(tokenId));
safeTransferFrom(seller, msg.sender, tokenId);
seller.transfer(msg.value);
tokenPrices[tokenId] = 0;
}
This structure separates concerns and maintains proper access control. Remember to add necessary checks and event emissions in a production environment.