Truffle: Question: Getting internal details of delegatecall function call

Created on 27 Aug 2020  路  5Comments  路  Source: trufflesuite/truffle

Lets say I have this contract:

contract Primary {
  function execute(address target) public returns(bytes) {
        (bool success, bytes memory returnedDataFromDelegateCall) = target.delegatecall(
            abi.encodeWithSignature("execute()")
        );
        if (!success) {
            if (data.length > 0) {
                revert(string(data));
            } else {
                revert("delegatecall failed");
            }
        }
        return returnedDataFromDelegateCall;
    }
}

then we deploy Target contract


contract Proposal {
    event Debug(address x);

    function executeProposal() public returns(address) {
        Dummy dummyInstance = new Dummy();
        dummyInstance.initialize();
        emit Debug(address(dummyInstance));
        return address(dummyInstance);
    }
}

Test.js

const target = await Target.new()
const primary = await Primary.new()
const receipt = await primary.execute(target.address)
console.log('???', receipt)

Questions:

  1. How to capture Debug event?
  2. How to read returnedDataFromDelegateCall
  3. How to read internal tx to see that the tx deployed a new contract?
needs investigated question

All 5 comments

Solution for 1.

const receipt = await primary.execute(target.address)
const debugLog = receipt.receipt.rawLogs[0]
const decodedLog = web3.eth.abi.decodeLog([{
   type: 'address',
   name: 'x'
}], debugLog.data, debugLog.topics[0])
console.log(decodedLog)

(In addition to the questions) So looks like there's a bug in @truffle/decoder.

@rstormsf I put together your example in a repo: https://github.com/gnidan-finds-bugs/missing-decoding

Note this failing test: https://github.com/gnidan-finds-bugs/missing-decoding/blob/master/test/decode.js#L7-L17

Hm, so, this is troublesome. The reason the decoder is failing here so much a bug so much this case falling outside its design.

Basically, the decoder assumes that only libraries will get delegatecalled. What's happening in this case is that the decoder sees an log originating from Primary and says, well, Primary doesn't define any events; it has no ancestors to inherit events from; and there are no libraries it could have delegatecalled that could emit an event on its behalf. Therefore, I can't decode this. It doesn't consider the possibility that Proposal could have been delegatecalled, and is emitting the event on behalf of Primary, because Proposal isn't a library.

I'm not entirely sure what the best way for us to handle this is. Like in order for this to work, the decoder would have to always consider any event as a possibility, regardless of where it's defined, but this could junk up the output with spurious decodings in some cases; there's a reason I didn't write the decoder that way initially.

That said, perhaps we could organize the output so that this isn't so much a problem. @gnidan has also suggested that perhaps this could be handled with some sort of option on decodeLog. I'm not sure what the right approach is here; we'll have to figure it out...

We should make a new separate issue for the decoder stuff to figure out how that should work. Leaving this issue open for now as well to look into all the open questions here.

We opened issues #3387 and #3388 to keep track of some problems raised here. Closing this in favor of those. Thanks @rstormsf!

Was this page helpful?
0 / 5 - 0 ratings