Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tracing: Improve decoding of functions output #6531

Merged
merged 7 commits into from
Dec 10, 2023

Conversation

klkvr
Copy link
Member

@klkvr klkvr commented Dec 6, 2023

Motivation

Currently, while decoding result of function execution in traces, the following is done:

  1. If function appears in ABIs of some contract seen during execution and identified by it's bytecode, ABI is taken from this contract bytecode.
  2. Otherwise, ABI is fetched from openchain.xyz

This works fine for decoding inputs, however, openchain.xyz does not (and can not) tell anything about output types of a function.

Due to this, outputs for any calls to external contract accessed by an interface in forked environment are not decoded at the moment.

An example of this behavior is following test:

pragma solidity 0.8.20;

import {Test} from "forge-std/Test.sol";

interface IERC20 {
    function balanceOf(address) external view returns (uint256);
}

contract TestTracing is Test {
    function test() public {
        vm.createSelectFork("mainnet");

        IERC20 usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
        usdc.balanceOf(address(0));
    }
}

Traces for it will look like this:

  [15795] TestTracing::test()
    ├─ [0] VM::createSelectFork("mainnet")
    │   └─ ← 0x0000000000000000000000000000000000000000000000000000000000000000
    ├─ [9815] 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48::balanceOf(0x0000000000000000000000000000000000000000) [staticcall]
    │   ├─ [2529] 0xa2327a938Febf5FEC13baCFb16Ae10EcBc4cbDCF::balanceOf(0x0000000000000000000000000000000000000000) [delegatecall]
    │   │   └─ ← 0x0000000000000000000000000000000000000000000000000000000000000000
    │   └─ ← 0x0000000000000000000000000000000000000000000000000000000000000000
    └─ ← ()

However, it should be decoded into this:

  [15795] TestTracing::test()
    ├─ [0] VM::createSelectFork("mainnet")
    │   └─ ← 0
    ├─ [9815] 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48::balanceOf(0x0000000000000000000000000000000000000000) [staticcall]
    │   ├─ [2529] 0xa2327a938Febf5FEC13baCFb16Ae10EcBc4cbDCF::balanceOf(0x0000000000000000000000000000000000000000) [delegatecall]
    │   │   └─ ← 0
    │   └─ ← 0
    └─ ← ()

And this gets much uglier when working with contracts returning tuples/addresses/bytes

Solution

There is already with_events function which is used for importing events from known contracts when constructing a CallTraceDecoder, so I've just added similar logic for functions as well

let mut builder = CallTraceDecoderBuilder::new()
.with_labels(result.labeled_addresses.iter().map(|(a, s)| (*a, s.clone())))
.with_events(local_identifier.events().cloned())
.with_verbosity(verbosity);

@klkvr klkvr changed the title forge: Improve decoding of functions output tracing: Improve decoding of functions output Dec 6, 2023
Copy link
Member

@DaniPopes DaniPopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a test with the example contract? I don't believe we have trace-specific tests, so a repro with this PR's number is OK. I think we'll want to separate those tho.

crates/forge/bin/cmd/test/mod.rs Outdated Show resolved Hide resolved
@DaniPopes
Copy link
Member

Also cc @onbjerg @Evalir tracing change

@klkvr klkvr force-pushed the klkvr/improved-output-decoding branch from 8075269 to ce65b62 Compare December 7, 2023 21:15
@klkvr klkvr requested a review from DaniPopes December 7, 2023 21:17
@DaniPopes
Copy link
Member

DaniPopes commented Dec 7, 2023

@klkvr Sorry I meant repro tests in crates/forge/tests/it/repros.rs

@klkvr
Copy link
Member Author

klkvr commented Dec 8, 2023

Hey @DaniPopes I am not sure if it makes sense to add this as a repro test

  1. It seems to me that it isn't possible to get known_contracts object and configure trace identifier with it while not changing repro testing logic too much

  2. Even if it is possible, the test will not really make much sense. I think test should check that

    traces are decoded correctly when testing through CLI

    rather than

    traces are decoded correctly when known contract abis are included into scope of call trace decoder

    The former is what this PR fixes, and the latter is how it does that, and this logic already worked before this PR

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is great!

the integration test is appropriate since we want to check the actual formatted trace output

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants