Skip to content

Commit

Permalink
migrate from ethabi to alloy
Browse files Browse the repository at this point in the history
  • Loading branch information
isum committed Feb 14, 2025
1 parent 157c291 commit f185888
Show file tree
Hide file tree
Showing 32 changed files with 2,633 additions and 486 deletions.
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

0 comments on commit f185888

Please sign in to comment.