Transferring NFTs and ERC20 tokens with an ERC721 contract

I have a contract that I want to use as an NFT marketplace. It extends ERC721URIStorage

contract NftMarketplace is ERC721URIStorage ...

In on function I use _transfer() to move an NFT from the seller to the buyer. Do I need to call "approve" or "approveForAll" first before using this? In my hardhat tests, there is no error using _transfer, but I don't know how to check my test accounts to see if the NFT actually moved.

My second question is about approving transfers of funds by a buyer in advance. The use case for this is the buyer making a bid or offer of a certain amount, and then the contract processing the payment later. I see this can be done by extending ERC20 contracts...but how can I bring that functionality into my contract that extends ERC721? Can I extends two contracts? Is that the best way?

Thanks!

To transfer your own tokens you don't need to call approve() or setApprovalForAll().

To transfer tokens owned by other address, that address needs to call approve() (which gives approval for a single token) or setApprovalForAll() (which gives approval for all tokens)

To check if the token moved, you can use ownerOf(), refer to the docs

Hi. Thanks I'd like to get into a few specifics if possible. So a sample use case is that a user with a wallet approves that my contract is allowed to move 10 matic from the user's wallet.

So I have a user which the approve function in my contract:

approve(spenderAddress, 10)

approve is part of the ERC20 interface.

Question #1
Let's say I am doing this through something like MetaMask. Is the spender address the user's account address? Is something like MetaMask also a smart contract that implements IERC20?

Question #2
If I implement approve, do I also need to implement all functions in the IERC20 interface?

Question #3
Right now my contract extends ERC721URIStorage. Can my contract also extend the ERC20 base class? If so, what is the syntax for this? If not what is the syntax to extends ERC721URIStorage but also implement the IERC20 interface?

Question #4
Let's say address1 has called approve(). I think the answer is yes, but just want to make sure that later the contract can call the transferFrom(address1, address2, 10) function from a public function I create, such as foo()? And the transfer will work for anyone that calls foo()? (Just trying to understand, not saying I will implement like this)

Question #5
Just to play Devil's advocate, what would stop someone from having BS or empty implementations for IERC20 and then call transferFrom, without actually doing anything.

MATIC is the native token of the polygon network and also exists as an ERC20 token on other networks. which one do you mean ?

Metamask is a wallet, the thing you use to send transactions (and this is how interaction with contracts happen)

The IERC20 interface defines what an ERC20 token looks like. you don't have to implement any of it unless you are implementing an erc20 token

ERC721 is used to represent an unique thing. ERC20 is used to represent things that are not unique ( not fungible vs fungible). doesn't make sense to implement both.

yes, the transferFrom() will work for anyone that calls foo(). Usually on a public functions the first arg (the tokens owner) is msg.sender. doesn't make much sense to use anything else. transferring tokens from a fixed address might be a huge security gap.

with a bs or empty implementation, you got a bs token. those exist by the ton

So if MATIC is erc20, it must have an approve function, right? So how could you write smart contract where a user approves the contract to withdraw matic at a later time.

An example would be like OpenSea where you can approve a number of tokens and then place bids on items. If the bid is a winner, the contract will withdraw later

correct. for a contract to be considered an erc20 token it must implement the functions defined in the interface (IERC20). an interface is just a description of functionality.

An example would be like OpenSea where you can approve a number of tokens and then place bids on items. If the bid is a winner, the contract will withdraw later

yup. You just described the common flow.

user (address) U wants to pay something with token T when interacting with contract C.

first the user needs to approve C to spend amount X (by calling approve on the token T, the spender is C)
then a bid is won or purchase is made (C calls transferFrom() on the token to transfer the payment, the timeframe between approve and transferFrom does not matter)

when using the network native token (ETH on the Ethereum network, BNB on BSC, MATIC on the polygon network, etc) you can send the amount with the transaction, it's a push. with erc20 tokens it needs to be a pull