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

Migrate from ethabi to alloy #5692

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,671 changes: 1,512 additions & 159 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ repository = "https://github.com/graphprotocol/graph-node"
license = "MIT OR Apache-2.0"

[workspace.dependencies]
alloy = { version = "0.11.1", features = ["dyn-abi", "json-abi"] }
anyhow = "1.0"
async-graphql = { version = "7.0.11", features = ["chrono", "uuid"] }
async-graphql-axum = "7.0.11"
Expand Down
21 changes: 10 additions & 11 deletions chain/ethereum/src/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Error;
use ethabi::{Error as ABIError, ParamType, Token};
use graph::abi;
use graph::blockchain::ChainIdentifier;
use graph::components::subgraph::MappingError;
use graph::data::store::ethereum::call;
Expand Down Expand Up @@ -104,13 +104,12 @@ pub enum EthereumRpcError {

#[derive(Error, Debug)]
pub enum ContractCallError {
#[error("ABI error: {0}")]
ABIError(#[from] ABIError),
/// `Token` is not of expected `ParamType`
#[error("type mismatch, token {0:?} is not of kind {1:?}")]
TypeError(Token, ParamType),
#[error("error encoding input call data: {0}")]
EncodingError(ethabi::Error),
#[error("ABI error: {0:#}")]
ABIError(anyhow::Error),
#[error("type mismatch, decoded value {0:?} is not of kind {1:?}")]
TypeError(abi::DynSolValue, abi::DynSolType),
#[error("error encoding input call data: {0:#}")]
EncodingError(anyhow::Error),
#[error("call error: {0}")]
Web3Error(web3::Error),
#[error("ethereum node took too long to perform call")]
Expand Down Expand Up @@ -1165,7 +1164,7 @@ pub trait EthereumAdapter: Send + Sync + 'static {
logger: &Logger,
call: &ContractCall,
cache: Arc<dyn EthereumCallCache>,
) -> Result<(Option<Vec<Token>>, call::Source), ContractCallError>;
) -> Result<(Option<Vec<abi::DynSolValue>>, call::Source), ContractCallError>;

/// Make multiple contract calls in a single batch. The returned `Vec`
/// has results in the same order as the calls in `calls` on input. The
Expand All @@ -1175,7 +1174,7 @@ pub trait EthereumAdapter: Send + Sync + 'static {
logger: &Logger,
calls: &[&ContractCall],
cache: Arc<dyn EthereumCallCache>,
) -> Result<Vec<(Option<Vec<Token>>, call::Source)>, ContractCallError>;
) -> Result<Vec<(Option<Vec<abi::DynSolValue>>, call::Source)>, ContractCallError>;

fn get_balance(
&self,
Expand Down Expand Up @@ -1204,9 +1203,9 @@ mod tests {
use graph::blockchain::TriggerFilter as _;
use graph::firehose::{CallToFilter, CombinedFilter, LogFilter, MultiLogFilter};
use graph::petgraph::graphmap::GraphMap;
use graph::prelude::ethabi::ethereum_types::H256;
use graph::prelude::web3::types::Address;
use graph::prelude::web3::types::Bytes;
use graph::prelude::web3::types::H256;
use graph::prelude::EthereumCall;
use hex::ToHex;
use itertools::Itertools;
Expand Down
100 changes: 31 additions & 69 deletions chain/ethereum/src/data_source.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use anyhow::{anyhow, Error};
use anyhow::{ensure, Context};
use graph::abi;
use graph::abi::EventExt;
use graph::abi::FunctionExt;
use graph::blockchain::{BlockPtr, TriggerWithHandler};
use graph::components::metrics::subgraph::SubgraphInstanceMetrics;
use graph::components::store::{EthereumCallCache, StoredDynamicDataSource};
Expand All @@ -13,8 +16,8 @@ use graph::env::ENV_VARS;
use graph::futures03::future::try_join;
use graph::futures03::stream::FuturesOrdered;
use graph::futures03::TryStreamExt;
use graph::prelude::ethabi::ethereum_types::H160;
use graph::prelude::ethabi::StateMutability;
use graph::prelude::web3::types::Address;
use graph::prelude::web3::types::H160;
use graph::prelude::{Link, SubgraphManifestValidationError};
use graph::slog::{debug, error, o, trace};
use itertools::Itertools;
Expand All @@ -30,9 +33,7 @@ use tiny_keccak::{keccak256, Keccak};
use graph::{
blockchain::{self, Blockchain},
prelude::{
async_trait,
ethabi::{Address, Event, Function, LogParam, ParamType, RawLog},
serde_json, warn,
async_trait, serde_json, warn,
web3::types::{Log, Transaction, H256},
BlockNumber, CheapClone, EthereumCall, LightEthereumBlock, LightEthereumBlockExt,
LinkResolver, Logger,
Expand Down Expand Up @@ -525,28 +526,28 @@ impl DataSource {
}
}

/// Returns the contract event with the given signature, if it exists. A an event from the ABI
/// Returns the contract event with the given signature, if it exists. An event from the ABI
/// will be matched if:
/// 1. An event signature is equal to `signature`.
/// 2. There are no equal matches, but there is exactly one event that equals `signature` if all
/// `indexed` modifiers are removed from the parameters.
fn contract_event_with_signature(&self, signature: &str) -> Option<&Event> {
fn contract_event_with_signature(&self, signature: &str) -> Option<&abi::Event> {
// Returns an `Event(uint256,address)` signature for an event, without `indexed` hints.
fn ambiguous_event_signature(event: &Event) -> String {
fn ambiguous_event_signature(event: &abi::Event) -> String {
format!(
"{}({})",
event.name,
event
.inputs
.iter()
.map(|input| event_param_type_signature(&input.kind))
.map(|input| input.selector_type().into_owned())
.collect::<Vec<_>>()
.join(",")
)
}

// Returns an `Event(indexed uint256,address)` type signature for an event.
fn event_signature(event: &Event) -> String {
fn event_signature(event: &abi::Event) -> String {
format!(
"{}({})",
event.name,
Expand All @@ -556,40 +557,13 @@ impl DataSource {
.map(|input| format!(
"{}{}",
if input.indexed { "indexed " } else { "" },
event_param_type_signature(&input.kind)
input.selector_type()
))
.collect::<Vec<_>>()
.join(",")
)
}

// Returns the signature of an event parameter type (e.g. `uint256`).
fn event_param_type_signature(kind: &ParamType) -> String {
use ParamType::*;

match kind {
Address => "address".into(),
Bytes => "bytes".into(),
Int(size) => format!("int{}", size),
Uint(size) => format!("uint{}", size),
Bool => "bool".into(),
String => "string".into(),
Array(inner) => format!("{}[]", event_param_type_signature(inner)),
FixedBytes(size) => format!("bytes{}", size),
FixedArray(inner, size) => {
format!("{}[{}]", event_param_type_signature(inner), size)
}
Tuple(components) => format!(
"({})",
components
.iter()
.map(event_param_type_signature)
.collect::<Vec<_>>()
.join(",")
),
}
}

self.contract_abi
.contract
.events()
Expand Down Expand Up @@ -628,7 +602,9 @@ impl DataSource {
})
}

fn contract_function_with_signature(&self, target_signature: &str) -> Option<&Function> {
fn contract_function_with_signature(&self, target_signature: &str) -> Option<&abi::Function> {
use abi::StateMutability;

self.contract_abi
.contract
.functions()
Expand All @@ -642,7 +618,7 @@ impl DataSource {
let mut arguments = function
.inputs
.iter()
.map(|input| format!("{}", input.kind))
.map(|input| input.selector_type().into_owned())
.collect::<Vec<String>>()
.join(",");
// `address,uint256,bool)
Expand Down Expand Up @@ -732,11 +708,7 @@ impl DataSource {
.into_iter()
.filter_map(|(event_handler, event_abi)| {
event_abi
.parse_log(RawLog {
topics: log.topics.clone(),
data: log.data.clone().0,
})
.map(|log| log.params)
.decode_log(&log)
.map_err(|e| {
trace!(
logger,
Expand Down Expand Up @@ -841,20 +813,15 @@ impl DataSource {
)
})?;

// Parse the inputs
//
// Take the input for the call, chop off the first 4 bytes, then call
// `function.decode_input` to get a vector of `Token`s. Match the `Token`s
// with the `Param`s in `function.inputs` to create a `Vec<LogParam>`.
let tokens = match function_abi.decode_input(&call.input.0[4..]).with_context(
|| {
let values = match function_abi
.abi_decode_input(&call.input.0[4..])
.with_context(|| {
format!(
"Generating function inputs for the call {:?} failed, raw input: {}",
&function_abi,
hex::encode(&call.input.0)
)
},
) {
}) {
Ok(val) => val,
// See also 280b0108-a96e-4738-bb37-60ce11eeb5bf
Err(err) => {
Expand All @@ -864,27 +831,22 @@ impl DataSource {
};

ensure!(
tokens.len() == function_abi.inputs.len(),
values.len() == function_abi.inputs.len(),
"Number of arguments in call does not match \
number of inputs in function signature."
);

let inputs = tokens
let inputs = values
.into_iter()
.enumerate()
.map(|(i, token)| LogParam {
.map(|(i, value)| abi::DynSolParam {
name: function_abi.inputs[i].name.clone(),
value: token,
value,
})
.collect::<Vec<_>>();

// Parse the outputs
//
// Take the output for the call, then call `function.decode_output` to
// get a vector of `Token`s. Match the `Token`s with the `Param`s in
// `function.outputs` to create a `Vec<LogParam>`.
let tokens = function_abi
.decode_output(&call.output.0)
let values = function_abi
.abi_decode_output(&call.output.0)
.with_context(|| {
format!(
"Decoding function outputs for the call {:?} failed, raw output: {}",
Expand All @@ -894,17 +856,17 @@ impl DataSource {
})?;

ensure!(
tokens.len() == function_abi.outputs.len(),
values.len() == function_abi.outputs.len(),
"Number of parameters in the call output does not match \
number of outputs in the function signature."
);

let outputs = tokens
let outputs = values
.into_iter()
.enumerate()
.map(|(i, token)| LogParam {
.map(|(i, value)| abi::DynSolParam {
name: function_abi.outputs[i].name.clone(),
value: token,
value,
})
.collect::<Vec<_>>();

Expand Down
Loading
Loading