Using delegateCall to batch NFT creation, authorization and marketplace listing in one transaction

I’m working on a smart contract that can handle multiple operations in a single transaction using delegateCall. What I want to do is create an NFT, give permission to a marketplace contract, and then list it for sale all at once.

function createAndListNFT(address payable receiver, string memory tokenUri, address nftContract, uint256 price, address marketContract)
    external
    payable
    returns (uint256)
{
    address nftCollection = nftContract;
    address marketplaceAddr = marketContract;

    uint256 newTokenId = executeNFTCreation(nftCollection, receiver, tokenUri);
    executeMarketplaceApproval(nftCollection, marketContract);
    executeListingForSale(marketplaceAddr, nftContract, newTokenId, price);

    return newTokenId;
}

function executeNFTCreation(address nftAddr, address owner, string memory metadataUri) internal returns(uint256 resultTokenId){
    INFTContract nftInterface = INFTContract(nftAddr);
    resultTokenId = nftInterface.createToken(payable(owner), metadataUri);
    return resultTokenId;
}

function executeMarketplaceApproval(address nftAddr, address approvedOperator) internal{
    bool approvalStatus = true;
    bytes memory callData = abi.encodeWithSignature("setApprovalForAll(address,bool)", approvedOperator, approvalStatus);
    bool callSuccess;
    
    assembly {
        let memPtr := mload(0x40)
        callSuccess := delegatecall(
            gas(),
            nftAddr,
            add(callData, 0x20),
            mload(callData),
            0,
            0
        )
    }
}

function executeListingForSale(address marketAddr, address tokenContract, uint256 tokenNumber, uint256 salePrice) internal{
    bytes memory callData = abi.encodeWithSignature("setSalePrice(address,uint256,uint256)", tokenContract, tokenNumber, salePrice);
    bool callSuccess;
    
    assembly {
        let memPtr := mload(0x40)
        callSuccess := delegatecall(
            gas(),
            marketAddr,
            add(callData, 0x20),
            mload(callData),
            0,
            0
        )
    }
}

The NFT creation part works fine but I’m getting errors with the delegateCall operations for approval and listing. The transaction fails during these steps. Has anyone dealt with similar issues when trying to batch these operations together?

DelegateCall won’t work here - you’re misunderstanding what it actually does. When you use delegateCall, the target contract’s code runs in YOUR contract’s context, not the NFT contract’s context. So the approval call tries to approve tokens that don’t even exist in your contract’s storage.

I hit this exact issue last year building a similar batching contract. Just use regular external calls instead of delegateCall. For approvals, have the user transfer the NFT to your contract first, then your contract can approve the marketplace and list it. Or set up a multicall pattern where users pre-approve your contract as an operator.

Your NFT creation works because you’re using a normal function call there, not delegateCall. Stick with that approach throughout your contract and you’ll dodge these storage context headaches completely.

wait, why are you using delegateCall here?

i get the storage context stuff others mentioned, but what made you think delegateCall was right for this? trying to save gas or need the code running in your contract’s context for some reason?

looks like you’re orchestrating calls between contracts (nft, marketplace) but delegateCall seems overkill. why not just use a multicall pattern?

also, something’s bugging me about your executeMarketplaceApproval function. you’re calling setApprovalForAll but who owns the NFT then? if it was minted to the receiver address, your contract can’t approve it anyway, right?

wouldn’t it make more sense to:

  1. mint to your contract first, then transfer after listing, or
  2. have users pre-approve your contract before calling this?

what’s your actual workflow? are users calling this directly or is there setup i’m missing?

yea, delegatecall won’t work here. ur mixing up contexts - when u delegatecall the nft contract, it runs in your contract’s storage space, not the nft’s. that’s why approval fails - the nft doesn’t exist in ur context. just use regular external calls instead, or set up a proxy pattern where users give ur contract operator permissions upfront.