1

Say I have

contract Bar {

    function blockingFunction() public pure returns (bool) {
        assembly {
            return(0,0x20)
        }
    }
}

contract Foo is Bar {

    function foo() public pure returns(bool) {
        bool result = blockingFunction();
        require(result == true, "msg");
        return result;
    }
}

Note that blockingFunction is a snip from a larger function that does something useful and actually returns a boolean.

The call blockingFunction() inside foo makes the following code unreachable, as return inside assembly blocks further execution. I first noticed this when hardhat complained as noted here. From the docs it says

return(p, s)   end execution, return data mem[p…(p+s))

My way of bypassing this was to call the function as this.blockingFunction() (or not inherit Bar but pass it inside Foo constructor and call blockingFunction externally via Bar from foo) which worked inside hardhat tests, but now when testing this on a testnet, I get the same problem. How do I bypass this and actually use the return value inside a contract?

There were a couple of answers saying require fails after the blockingFunction call. I would have noticed this either in hardhat test or with testnet.

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
kuco 23
  • 786
  • 5
  • 18
  • I am not sure that opcode returns a bool. That said if you want to get a value from a contract it needs to be from a deployed contract (contract instance). From your question it isn't clear to me what the desired outcome is. – ruby_newbie Dec 08 '22 at 21:40
  • `Bar` is a snip from a larger contract that returns a bool using `return(0, 0x20)`. The thing is I can deploy Bar as a separate contract and call `blockingFunction` from `Foo` but it is still blocking the code below. I just want to be able to use the return value from `blockingFunction` inside the `foo` function. Whatever I do, the whole execution always stops at `return(0,0x20)`. – kuco 23 Dec 08 '22 at 21:48

2 Answers2

1

return opcode returns from entire contract execution not only function execution. Instead just return from the assembly like this

contract Bar {
    function blockingFunction() public pure returns (bool val) {
        assembly {
            let freePointer := mload(0x40)
            // store true
            mstore(freePointer,true)
            // return the stored value without return opcode
            val:=mload(freePointer)
        }      
    }  
}

contract Foo is Bar {
    function foo() public pure returns(bool) {
        bool result = blockingFunction();
        require(result == true, "msg");
        return result;
    }
}

proof of work:

enter image description here

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
-2
assembly {
    return(0,0x20)
}

This snippet returns the value from the memory slot 0, length of the returned value is 32 bytes (hex 0x20 == dec 32).

While the Yul docs page says "end execution", it can be represented as "end execution of this particular function - not of the whole transaction". In other words, the return statement in your blockingFunction() itself does not make the rest of foo() unreachable.

The Layout in Memory docs page mentions the slot 0 as "scratch space for hashing methods" and generally recommends using the memory starting at slot 4 (where the Free memory pointer initially points to) instead of slot 0, as the slot 0 might contain some unexpected values.

Your code does not assign any value to memory slot 0, so all it reads is really just the value from the scratch space.

I'm guessing that your local emulator returned non-zero value from the memory slot 0, which assigned the value of bool result to true, and effectively passed the require() condition in your foo() function. And that the testnet returned 0 value, which caused bool result to be false and fail the require() condition.

Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • I should have mentioned that in actuallity assembly's `return(0,0x20)` is a part of a larger function that actually makes sense and assigns data to memory slot. Without using `this` in front of the `blockingFunction()` call, even hardhat says the code after is not reachable. And during hardhat testing `require` failure would generate an error where the reason (i.e. `msg`) would be visible. – kuco 23 Dec 11 '22 at 12:58