Payable transactions not working

Hello, I am not able to transfer ether between accounts and the payable function doesn’t work as expected either. But when the function is not payable and we are not transferring ether the program works fine.

I am using Hardhat.

I have a simple contract with an increment function that simply increments the counter variable.
My contract :

pragma solidity ^0.8.0;

import "@opengsn/contracts/src/BaseRelayRecipient.sol";

contract Counter is BaseRelayRecipient {
    
    uint private counter;
    address public lastCaller;
    address public owner;

    event Increment(address indexed by, uint256 to);

    constructor(address _forwarder) {
        _setTrustedForwarder(_forwarder);
        counter = 0;
        owner = _msgSender();
	}

    function increment() public payable{
        require(msg.value == 2 ether, "please send 2 ether to increment");
        counter+=1;
        lastCaller = _msgSender();
        payable(owner).transfer(msg.value);
        emit Increment(_msgSender(), counter);
    }

    function getCounter() public view returns(uint) {
        return counter;
    }

    function versionRecipient() external pure override returns (string memory) {
        return "2.2.6";
    }
}

I am using the naive paymaster:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

import "@opengsn/contracts/src/BasePaymaster.sol";
import "@opengsn/contracts/src/forwarder/IForwarder.sol";


// accept everything.
// this paymaster accepts any request.
//
// NOTE: Do NOT use this contract on a mainnet: it accepts anything, so anyone can "grief" it and drain its account

contract Paymaster is BasePaymaster {

    function versionPaymaster() external view override virtual returns (string memory){
        return "2.2.3+opengsn.test-pea.ipaymaster";
    }

    event SampleRecipientPreCall();
    event SampleRecipientPostCall(bool success, uint actualCharge);

    function preRelayedCall(
        GsnTypes.RelayRequest calldata relayRequest,
        bytes calldata signature,
        bytes calldata approvalData,
        uint256 maxPossibleGas
    )
    external
    override
    virtual
    returns (bytes memory, bool) {
        (signature);
        _verifyForwarder(relayRequest);
        (approvalData, maxPossibleGas);
        emit SampleRecipientPreCall();
        return ("no revert here",false);
    }

    function postRelayedCall(
        bytes calldata context,
        bool success,
        uint256 gasUseWithoutPost,
        GsnTypes.RelayData calldata relayData
    )
    external
    override
    virtual
    {
        (context, gasUseWithoutPost, relayData);
        emit SampleRecipientPostCall(success, gasUseWithoutPost);
    }

    function deposit() public payable {
        require(address(relayHub) != address(0), "relay hub address not set");
        relayHub.depositFor{value:msg.value}(address(this));
    }

    function withdrawAll(address payable destination) public {
        uint256 amount = relayHub.balanceOf(address(this));
        withdrawRelayHubDepositTo(amount, destination);
    }
}

I am setting the forwarder address and relay hub address properly and also funding the paymaster in two separate scripts which work fine.

In my frontend I am using the following code to interact with the increment function and send 2 ethers.

const gsnConfig = {
  paymasterAddress: paymasterAddress,
};
const gsnProvider = RelayProvider.newProvider({
  provider: window.ethereum,
  config: gsnConfig,
});
await gsnProvider.init();
const provider = new ethers.providers.Web3Provider(gsnProvider);
const signer = provider.getSigner();
const contract = new ethers.Contract(counterAddress, Counter.abi, signer);

await contract.increment({
  value: ethers.utils.parseUnits('2', 'ether'),
  gasPrice: 0,
  gasLimit: 1e6,
});

Metamask prompts to sign the transaction and nothing happens then. In the hardhat console I get the following response: The from address is of the admin address which is used to deploy the contracts and fund the paymaster. The to address is the relayHub adderss.

Contract call:       <UnrecognizedContract>
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9

But for non-payable functions, my program can interact with it without any problems.

Hey, GSN doesn’t support payable functions. The issue is transferring native msg.value, this value has to forwarded somehow. It might be possible to create a workaround with the Forwarder/Paymaster, I’ve never seen a working example of this though.

1 Like

Thanks for the reply!
That’s very unfortunate. So is there a way to send ether from one account to another without having the user pay gas?