postRelayCall stops executing

I have an application where my postRelayCall sells tokens given to it during the execution of the target address (similar to TokenPaymaster). Below is some pseudo code of the contract interactions. If I need to add a more concrete example I can, but hopefully this gets the idea across. I’ve set my gasLimit to a little bellow the blocklimit.

The issue I’m having is the “postRelayCall” seems to stop executing during the “dex.sell” (which I have MOC’d out) but the line it stops executing seems to be coupled with the amount of lines “dex.sell” has?? I.E if I comment out the line before “dex.sell” the program will go one line further. (Side Note, I’ve been using hardhat console.log to determine which line the program “stops executing”. This might be giving me bad readings but it seems to be working). ALSO, I’ve tested calling “dexSell” directly from inside the target contract, and it works! I’m not sure why it would work while executing in the context of my contract, but not work when executing as the “postRelayCall”. Any suggestions?

contract Paymaster is BasePaymaster {
    TargetContract targetCon
    address public latestToken;
    uint public latestAmount;

    preRelayCall() {

    postRelayCall() {
        // calculate ETHER needed 
        uint charge = relayHub.calculateCharge(gasUseWithoutPost.add(gasUsedByPost), relayData);
        uint refund = this.dexSell(latestToken, latestAmount, charge)
        IERC20(token).approve(address(targetCon), refund)
        targetCon.refund(token, refund)

    dexSell(latestToken, latestAmount, charge) {
        uint refund = dex.sell(latestToken, latestAmount, charge)
        IERC20(latestToken).transferFrom(address(dex), address(this), refund)
        return refund

    sellTokens(tokenAddr, amount) {
        IERC20(tokenAddr).transferFrom(address(targetCon), address(this), amount)
        latestToken = tokenAddr
        latestAmount = amount

contract TargetContract is BaseRelayRecipient {
    bool isGSNReq;
    address p // paymasterAddr

   targetFn(token, amount) { // transfer sender tokens and sell if user is using gsn
        IERC20(token).transferFrom(_msgSender(), address(this), amount)
        if (isGSNReq) {
            IERC20(token).approve(address(p), amount) // approve tokens to be used in postRelayCall
            Paymaster(p).sellTokens(token, amount)
            // Paymaster(p).dexSell(...) // THIS WORKS?! 

    setIsGSNRequest() {
        isGSNReq = true;

   refund(token, amount) {
      IERC20(token).transferFrom(address(p), address(this), amount);
      isGSNReq = false;

Here is a TX on etherscan: Rinkeby Transaction Hash (Txhash) Details | Etherscan
it says success, but it mentions an error in the contract.

UPDATE: Looking at the docs there is a “getGasLimits()” function we can override in version 2.0.1, but I’m using 2.2.0 and the function doesn’t seem to exist on the IPaymaster anymore. My theory is the postRelayCall runs out of gas. Furthermore, I can see that gasLeft == 109018 at the beginning of the postRelayCall which is much less gas than my defined “gasLimit” of 10450000.

ANSWER: in 2.2.0 there is a function called getGasAndDataLimits on the BasePaymaster. I had to override it in my Paymaster class:

    function getGasAndDataLimits()
    returns (
        IPaymaster.GasAndDataLimits memory limits
    ) {
        uint postCallLimit = 610000; // POST_RELAYED_CALL_GAS_LIMIT
        return IPaymaster.GasAndDataLimits(

Sorry we didn’t return to you earlier…
Did you manage to solve the problem?

Anyway, tenderly can show gsn transactions nicely: Tenderly Dashboard