Skip to content

ethereum: Fix adapter to filter proxy calls - #3219

Merged
evaporei merged 3 commits into
masterfrom
otavio/fix-ethabi
Feb 7, 2022
Merged

evaporei merged 3 commits into
masterfrom
otavio/fix-ethabi

Conversation

@evaporei

@evaporei evaporei commented Feb 4, 2022 •

Copy link
Copy Markdown
Contributor

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

@leoyvens leoyvens left a comment

Copy link
Copy Markdown
Contributor

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 thread chain/ethereum/src/adapter.rs Outdated
Comment on lines +309 to +313
if let None = v {
return false;
}

let signature = &v.unwrap().1;

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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.

Comment thread chain/ethereum/src/adapter.rs Outdated
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
Copy Markdown
Contributor

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
Copy Markdown
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 thread chain/ethereum/src/adapter.rs Outdated
Comment on lines +328 to +330
let wrong_input_size = (call.input.0.len() - 4) % 32 != 0;

correct_fn && !wrong_input_size

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
@lutter
lutter deleted the otavio/fix-ethabi branch November 12, 2025 21:55
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