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

ethereum: Fix adapter to filter proxy calls #3219

Merged
merged 3 commits into from
Feb 7, 2022
Merged

Conversation

evaporei
Copy link
Contributor

@evaporei evaporei commented Feb 4, 2022

Without this the line that does function_abi.decode_input(&call.input.0[4..]) was failing with the Err(InvalidData).

For some reason some of the OpenSea Proxy contracts don't get recognized as DELEGATECALL, instead they are seen as CALLs, but you can identify them by the input having the wrong size since it's a fallback function call.

A scenario to exemplify the problem, can be seen in this transaction: 0x4499dc75facb4cd27894196aac00bcb0ffbe3d64de87105878451d5637662f90.

We would get the traces for the block 14043345 (0xD648D1) for the contract 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b. Then we would find two CALLs for the atomicMatch_ function. However the first one would have the wrong size (fail at decoding) and the second one would be correct.

That's because of the proxy and because of how fallbacks are compiled from Solidity (omitting the function signature, aka a feel bytes less).

Ethereum ABI (d)ecoding docs: https://docs.soliditylang.org/en/v0.8.11/abi-spec.html.

Fixes #3194

⚠️ Do not merge this yet, I'm indexing an OpenSea subgraph locally to verify I'm not filtering any triggers that shouldn't be filtered, just to be sure ⚠️

@evaporei evaporei requested a review from lutter February 4, 2022 04:02
@evaporei evaporei self-assigned this Feb 4, 2022
Without this the line that does `function_abi.decode_input(&call.input.0[4..])`
was failing with the Err(InvalidData).

For some reason some of the OpenSea Proxy contracts don't get recognized
as DELEGATECALL, instead they are seen as CALLs, but you can identify
them by the input having the wrong size since it's a fallback function
call.

A scenario to exemplify the problem, can be seen in this transaction:
0x4499dc75facb4cd27894196aac00bcb0ffbe3d64de87105878451d5637662f90

We would get the traces for the block 14043345 (0xD648D1) for the
contract 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b. Then we would
find two CALLs for the `atomicMatch_` function. However the first one
would have the wrong size (fail at decoding) and the second one would be
correct.

That's because of the proxy and because of how fallbacks are compiled
from Solidity (omitting the function signature, aka a feel bytes less).
@evaporei evaporei requested review from Jannis and leoyvens and removed request for Jannis and lutter February 4, 2022 04:45
Copy link
Collaborator

@leoyvens leoyvens left a comment

Choose a reason for hiding this comment

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

Approved after a few refactors

Comment on lines 309 to 313
if let None = v {
return false;
}

let signature = &v.unwrap().1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if let None = v {
return false;
}
let signature = &v.unwrap().1;
let signature = match v {
None => return false,
Some(v) => &v.1,
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've wrapped this suggestion with the self.contract_addresses_function_signatures.get(&call.to) function call, so there's no more v let definition.

let correct_fn = signature.contains(&call.input.0[..4]);
// Not multiple of 32, wrong length to decode parameters.
// This is probably a delegatecall disguised as a regular call
// via fallback.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reference you can link here if somebody wants to better understand how this happens? Maybe link an example trace in etherscan?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've improved the comment, can you check if it's good now please?

Comment on lines 328 to 330
let wrong_input_size = (call.input.0.len() - 4) % 32 != 0;

correct_fn && !wrong_input_size
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let wrong_input_size = (call.input.0.len() - 4) % 32 != 0;
correct_fn && !wrong_input_size
let correct_input_size = (call.input.0.len() - 4) % 32 == 0;
correct_fn && correct_input_size

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 😊

@evaporei evaporei merged commit e35ac43 into master Feb 7, 2022
evaporei added a commit that referenced this pull request Feb 8, 2022
* ethereum: Fix adapter to filter proxy calls

Without this the line that does `function_abi.decode_input(&call.input.0[4..])`
was failing with the Err(InvalidData).

For some reason some of the OpenSea Proxy contracts don't get recognized
as DELEGATECALL, instead they are seen as CALLs, but you can identify
them by the input having the wrong size since it's a fallback function
call.

A scenario to exemplify the problem, can be seen in this transaction:
0x4499dc75facb4cd27894196aac00bcb0ffbe3d64de87105878451d5637662f90

We would get the traces for the block 14043345 (0xD648D1) for the
contract 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b. Then we would
find two CALLs for the `atomicMatch_` function. However the first one
would have the wrong size (fail at decoding) and the second one would be
correct.

That's because of the proxy and because of how fallbacks are compiled
from Solidity (omitting the function signature, aka a feel bytes less).

* ethereum: Apply code review suggestions for the adapter

* ethereum: Elaborate/improve comment about call input size
evaporei added a commit that referenced this pull request Feb 8, 2022
* ethereum: Fix adapter to filter proxy calls

Without this the line that does `function_abi.decode_input(&call.input.0[4..])`
was failing with the Err(InvalidData).

For some reason some of the OpenSea Proxy contracts don't get recognized
as DELEGATECALL, instead they are seen as CALLs, but you can identify
them by the input having the wrong size since it's a fallback function
call.

A scenario to exemplify the problem, can be seen in this transaction:
0x4499dc75facb4cd27894196aac00bcb0ffbe3d64de87105878451d5637662f90

We would get the traces for the block 14043345 (0xD648D1) for the
contract 0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b. Then we would
find two CALLs for the `atomicMatch_` function. However the first one
would have the wrong size (fail at decoding) and the second one would be
correct.

That's because of the proxy and because of how fallbacks are compiled
from Solidity (omitting the function signature, aka a feel bytes less).

* ethereum: Apply code review suggestions for the adapter

* ethereum: Elaborate/improve comment about call input size
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.

Failure on Call input decoding
2 participants