withdrawPayments not withdrawing

My question; How to get the withdrawPayments, out-of-the-box, no modification to the original OZ source code with JavaScript?

Case, currently I have a smart contract creating ERC721. To mint, I call the following safeMint function:

function safeMint(address to, string memory uri) 
        public
        payable
        onlyOwner 
        returns (uint256)
        {
        
        uint256 MINT_PRICE = 5.00 ether;
        
        require(msg.value == MINT_PRICE, "Transaction value did not equal the mint price");

        uint256 tokenId = s_tokenIdCounter.current();

        s_tokenIdCounter.increment();

        _safeMint(to, tokenId);

        uri = string(abi.encodePacked(uri, tokenId.toString()));
        _setTokenURI(tokenId, uri);

        _asyncTransfer(address(this), msg.value);

        return tokenId;
    }

I use JavaScript to trigger the minting. The amount send is, as you can see, transferred to an Escrow contract using the _asyncTransfer.

To double check, I query the Escrow contract using payments() from the OZ PullPaymentUpgradeable.sol. I can see that ETH is transferred and stored to the Escrow contract using contract.payments(contractAddress) in JavaScript from (again) the PullPaymentUpgradeable.sol.
Query the, in my case Proxy Contract, using JavaScript (const proxyBalance = await ethers.provider.getBalance(contractAddress):wink: results into zero (0).

Guess everything works as might be expected...!?

But! I can't get the withdrawPayments() to work. Nothing gets withdrawn nor send.
So I dived into the Escrow contracts all the way to the withdraw() function in the EscrowUpgradeable.sol, but along the way I get mixed up what the payee should be.
In the PullPaymentUpgradeable.sol it says; @param payee Whose payments will be withdrawn.
In the EscrowUpgradeable.sol it says; @param payee The address whose funds will be withdrawn and transferred to.

The EscrowUpgradeable.sol sounds like a circle reference...? When the Payee address is the address both withdrawn as transferred to. However in the contract code I don't see where the recipient address is entered, only the Payee param is mentioned. I might overlook something here...?

Nonetheless I tried numerous things to try and understand how everything works. And tried the following values when triggering the withdrawPayments(payee) function with the following values:

  1. parameter payee = Random (but!) valid Recipient Dummy Contractaddress given by Hardhat, no errors, but no ETH is transferred nor withdrawn.
  2. parameter payee = Contractaddress, but I get an error; 'CurrentContractAddress', the code fails with the error; Address: unable to send value, recipient may have reverted'.

Either way, I can't seem to get the withdrawPayment to work. What am I missing here?

Hope you all can still follow along...

Any thoughts, help and/or solution :wink: is very welcome!

Already thanks in advance.

:computer: Environment

Hardhat

Payee and recipient are the same concept.

Can you share your Javascript code?

Sure! See code below. Might contain a bit of rubbish unused imports as this is a testing script.

const { ethers } = require("hardhat");
const { accounts } = require("@openzeppelin/test-environment");
async function main () {

    provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545/')
    const privateKey = process.env.SIGNER_PRIVATE_KEY
    const signer = new ethers.Wallet(privateKey, provider)

    // Address given by Hardhat on deployment
    const contractAddress = '0xCD8a1C3ba11CF5ECfa6267617243239504a98d90';

    // Get and attach contrect
    const tt_Mint = await ethers.getContractFactory('tt_mint');
    const contract = await tt_Mint.attach(contractAddress);

    /*
    Mint a dummy NFT
    */
    const baseTokenURI = 'some Url';
    
    // Note! You cannot mint when the contract is Paused!
    const mintResponse = await contract.safeMint(
        signer.address, baseTokenURI,
        {
            value: ethers.utils.parseEther("5.00"),
        });

    // The transactionhash you can check on Polyscan when deployed.
    console.log(`Transaction Hash: ${mintResponse.hash}`);

    // Get User balance BEFORE withdraw of the contract. 
    const userBalance = await ethers.provider.getBalance(signer.address);
    console.log('Users balance: ', ethers.utils.formatEther(userBalance), "ETH");

    // Check Proxy contract balance, should (and is), zero (0)
    const proxyBalance = await ethers.provider.getBalance(contractAddress);
    console.log('Proxy balance: ', ethers.utils.formatEther(proxyBalance), "ETH");

    // Check Escrow contract balance, should and does increase with each Mint. 
    const escrowBalance = await contract.payments(contractAddress);
    console.log('Escrow balance: ', ethers.utils.formatEther(escrowBalance), "ETH");

    // Attempt Withdraw contract, either fails or does nothing. 
    const withdrawResponse = await contract.withdrawPayments(signer.address);
    console.log(withdrawResponse)

    // Check user balance, AFTER withdraw. 
    const updatedUserBalance = await ethers.provider.getBalance(signer.address);
    console.log('User address: ', signer.address)
    console.log('Users balance: ', ethers.utils.formatEther(updatedUserBalance), "ETH");
  }
  
main()
.then(() => process.exit(0))
.catch(error => {
    console.error(error);
    process.exit(1);
});

I think I might have found the problem. I expect there is a mix up with payee address and the (escrow) contract address... Let me trie to explain my thoughts on this...

  1. First I trigger withdrawPayments with payee (some user address) as the one and only parameter. The payee equals the recipient address. I checked in advance, the payee funds equals zero (0).

  2. This withdrawPayments then takes the payee address and triggers the _escrow contract and within that contract the withdraw function. Again taking the parameter payee with fund 0.

  3. The withdraw function, defines a payment variable which equals the current _deposits. This value is retrieved using _deposits with the payee address as its parameter. But! Hence the value of the payee address is still zero (0).Thus payment value quals 0.

  4. Next payment is set to zero (0). But it was already zero (0). And the sendValue function is trigger using the payment variable as its parameter. Which is still zero (0).

Thus nothing is changed, no values are updated what so ever.

What would have made sense in step 3 here is that the _deposit is called using the contract address as its parameter. Just like the function payments, which also (in the end) triggers the _deposits taking the contract address as its parameter.

But currently, taking the withdrawPayment route, the _deposits cannot take contract address as its parameter as it only got the payee address from the start.

Should the withdraPayment function not also take in the contract address parameter? <- potential bug and thus solution?

Or am I still missing something here...

Oh, I see... If you do this:

You are doing an async transfer to the token contract itself. You are in theory then able to call token.withdrawPayments(token.address) but the token doesn't have a receive function so it will reject receiving the funds and the function call will revert.

It sounds like you want the funds to go to your deployer account (signer in the JavaScript code). In that case, you need a variable in your contract to hold this address, let's call it deployer, and your transfer would look like _asyncTransfer(deployer, msg.value).

@frangio That definitely solved my problem! Although I did had to go through some Hardhat struggles. Not sure but Hardhat is at times persistent with contracts. Think it has to do with its artifacts not being updated.