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

feat(zevm): add ret bytes to error when zEVM contract call reverts #753

Merged
merged 3 commits into from
Jun 29, 2023

Conversation

brewmaster012
Copy link
Collaborator

@brewmaster012 brewmaster012 commented Jun 28, 2023

closes #742

This PR adds return code of a zEVM contract if it's called from external chains and reverts. The return code carries the "REVERT" opcode and its parameters, from which revert error messages can be reconstructed (perhaps with help of contract ABI if it's a typed error instead of a string).

Here's an example contract that always reverts:

function onCrossChainCall(address zrc20, uint256 amount, bytes calldata message) external override {
        revert("hey this is an error message from revert()");
}

when called from external chains, the CCTX aborts with error message:

"status_message": "contract call failed: method 'depositAndCall', contract '0x91d18e54DAf4F677cB28167158d6dd21F6aB3921': execution reverted: ret 0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002a686579207468697320697320616e206572726f72206d6573736167652066726f6d20726576657274282900000000000000000000000000000000000000000000: evm transaction execution failed: Unable to deposit ZRC20 ",

The ret code can be interpreted:

0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002a686579207468697320697320616e206572726f72206d6573736167652066726f6d20726576657274282900000000000000000000000000000000000000000000

Let's break it down and interpret the different parts:

  1. The first four bytes, 0x08c379a0 is the keccak256 hash of type "Error(string)". This indicates the revert() was called with a string, rather than a typed error.

  2. The rest of the data, 00000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002a686579207468697320697320616e206572726f72206d6573736167652066726f6d20726576657274282900000000000000000000000000000000000000000000, represents the encoded error parameters. Let's decode it further:

    • The first 32 bytes (0000000000000000000000000000000000000000000000000000000000000000) represent the length of the error string, which is zero in this case.

    • The next 32 bytes (0000000000000000000000000000000000000000000000000000000000000002) represent the length of the array containing two elements.

    • Following that, we have the two array elements:

      • The first array element is 0x686579207468697320697320616e206572726f72206d6573736167652066726f6d207265766572742829. This is the hexadecimal representation of the string "hey this is an error message from revert()".

      • The second array element is 0x0000000000000000000000000000000000000000000000000000000000000000, indicating the end of the array.

Therefore, the reason string for the revert is "hey this is an error message from revert()".

Please note that this interpretation assumes the error parameters are encoded using the standard Ethereum ABI encoding.

@brewmaster012
Copy link
Collaborator Author

FYI @fadeev @andresaiello

@fadeev
Copy link
Member

fadeev commented Jun 28, 2023

This is awesome! I'll modify the tasks to be able to interpret return codes like this.

@andresaiello
Copy link
Contributor

really great job! Will this workaround work for custom errors? or how should we handle that?

Copy link
Contributor

@kevinssgh kevinssgh left a comment

Choose a reason for hiding this comment

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

Looks good

@@ -399,7 +400,7 @@ func (k Keeper) CallEVMWithData(
}

if res.Failed() {
return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, res.VmError)
return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, fmt.Sprintf("%s: ret 0x%x", res.VmError, res.Ret))
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be the actual change.

@brewmaster012 brewmaster012 merged commit da2c0c8 into develop Jun 29, 2023
@brewmaster012 brewmaster012 deleted the refactor-deposit-and-call branch June 29, 2023 22:28
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.

bug: calling zEVM contract from external chains via SystemContract does not return revert string
4 participants