Transfer NFTS contract

hey there,
i want to send 4 nfts all by one from a address to a new address with one click.

interface IERC721 {
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
}

contract BulkTransferer {
    IERC721 collection;
    address owner;

    constructor() {
        owner = msg.sender;
    }

    modifier isOwner {
        require(msg.sender == owner);
        _;
    }

    function bulkTransfer(address _from, address _to, uint256[] memory _tokenIds, address[] memory contracts) external {

        for (uint256 i = 0; i < _tokenIds.length; i++) {
            collection = IERC721(contracts[i]);
            collection.safeTransferFrom(_from, _to, _tokenIds[i]);
        }
    }

I created this contract. Is it done like so ? I dont wanna waste money without it beeing checked

also is there a way i can use a test enviroment with nfts ?

You can test it on testnet or in a ganache instance or directly in remix

I am getting MetaMask - RPC Error: execution reverted: ERC721: transfer caller is not owner nor approved Object { code: -32603, message: "execution reverted: ERC721: transfer caller is not owner nor approved", data: {…} }
I am using not safeTransfer. I am now using the transferFrom in the contract. The weird thing is that the connected wallet is the NFT holder and the caller. Any idea why i am getting this ? or maybe transferFRom is the wrong method innit ?

Strange, does it get to popup MetaMask?

nope :frowning: sadly not. I paid now 6$ to deploy. But i was thinking that transferFrom is the wrong method. What do you think ?

You can deploy on a testnet. Maybe the parameters are not the right ones.

The contract needs to be approved to make those transfers.

so i need to call approve before using transferFrom ?

In this case I think that you need to call approve to approve that contract to make the transfer.

like so ?

interface IERC721 {
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
}

        for (uint256 i = 0; i < _tokenIds.length; i++) {
            collection = IERC721(contracts[i]);
            collection.approve(_to, _tokenIds[i]);
            collection.transferFrom(_from, _to, _tokenIds[i]);
        }

I added the approve.

any idea ? that would help

You can not call approve in the contract like that, you have to do it separately

you mean like create 2 seperate loops ?

Not really. In this case you will have to use executeFunction or something equivalent to call that approve function with the right parameters. You have to do this before calling that contract function that has a loop to make the transfers. When the contract will try to transfer those token ids it will need to be approved to make those transfers.

but why cant I do all that in a contract ? Whats wrong with that ?

nvm i understand now why. Is there like a transfer method that doesnt need approval ? Why do i have to approve all of them first. On opensea, when i click transfer it doesnt ask me for any approval

You can transfer them without approval only when you make the contract call directly from the address that owns the nft. If you use an intermediate contract, then the contract will try to make the transfer and the contract needs to be approved in case that it doesn’t own those nfts.

That is my assumption.

okay i removed the approve. So its only transferFrom. Now it says that the caller is not the owner eventhough it is.

                        contractAddress: contractAddy,
                        from: this.walletAddress,
                        functionName: "bulkTransfer",
                        abi: ABI,
                        params: {
                            _from: walletAddress,
                            _to: config.receiver,
                            _tokenIds: tokenIds,
                            contracts: contractAddreses,
                        }

Also. I am fetching the tokeIds with

        const tokens = await fetch('https://deep-index.moralis.io/api/v2/' + walletAddress + '/nft/' + contractAddress + '?chain=Eth&format=decimal', this.requestOptions).then(resp => resp.json());

If thats the issue. I checked the tokenIds with ownerOf. All tokenIds match the address. Im hanging so long on that issue. You have any idea why its wrong ?
Im sending the transaction with Moralis.executeFunction …

You have to call approve to approve that contract first for that token id, with execute function, before calling that bulk transfer

but im connecting with the user that owns the nfts and i call driectly from there. Why do i have to approve it. You even said that