0

In Web3 we can do TestContract.methods.myMethod(myParam).call() to call a state changing function without executing it. But I am wondering if this is possible in Solidity or inline-assembly? So in other words I want to call another contract's function, but not actually execute it, only get the return value or revert message.

I could add another public view function of course, but I am running out of space and my contract is getting too large. Alternatively, I know there are several workarounds for contracts being too large, but nothing really ideal.

2

You can try, but my intuition tells me that it will revert, because your non-constant function will be executed with 0 gas.

For example, suppose that your function is:

function myFunc(address x, uint256 y) public returns (uint256);

Then you can use:

bytes4 private constant MY_FUNC_SELECTOR = bytes4(uint256(keccak256("myFunc(address,uint256)") >> (256 - 4 * 8)));

function callMyFunc(address dest, address x, uint256 y) public view returns (uint256) {
    uint256[1] memory ret;
    bytes memory data = abi.encodeWithSelector(MY_FUNC_SELECTOR, x, y);

    assembly {
        let success := staticcall(
            gas,           // gas remaining
            dest,          // destination contract address
            add(data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
            mload(data),   // input length (loaded from the first 32 bytes in the `data` array)
            ret,           // output buffer
            32             // output length
        )
        if iszero(success) {
            revert(0, 0)
        }
    }

    return ret[0];
}

Note that if myFunc returns a tuple of 2 items, for example, then you'll need to modify the above:

function callMyFunc(...) public view returns (uint256, uint256) {
    uint256[2] memory ret;
    ...
    64 // output length
    ...
    return (ret[0], ret[1]);
}
| improve this answer | |
  • Thanks, staticcall seems like what I was searching for. I've tried to run your example, but I am still having two problems. 1) The return value seems to be empty always. 2) How do I get the revert message? You can try pastebin.com/YgFE30Ww at Remix. – Markus - soliditydeveloper.com Apr 16 at 6:09
  • @soliditydeveloper.com: Do you mean when you call this function from the on-chain (i.e., from a contract) or from the off-chain (e.g., from a web3.js-based script)? – goodvibration Apr 16 at 6:18
  • @soliditydeveloper.com: The return value is "empty always", because you removed the if iszero(success) { revert(0, 0) } part from the solution that I have suggested! If you left it in the code, then you would observe that the transaction always reverted, just like I had speculated at the beginning of my answer. – goodvibration Apr 16 at 6:24
  • Yeah, I removed the part, because it always reverts the transaction. (well unless the call itself reverts). But so what's the point of it if it just reverts the transaction? – Markus - soliditydeveloper.com Apr 16 at 8:05
  • 1
    @soliditydeveloper.com: That's exactly what I pointed out at the very top of my answer - you cannot execute a non-constant function from a constant function because the latter would be calling the former with 0 gas. – goodvibration Apr 16 at 8:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.