Skip to content

Commit 05532f0

Browse files
committed
migrate from ethabi to alloy
1 parent 157c291 commit 05532f0

File tree

31 files changed

+1924
-348
lines changed

31 files changed

+1924
-348
lines changed

Cargo.lock

Lines changed: 1305 additions & 106 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ repository = "https://github.com/graphprotocol/graph-node"
2626
license = "MIT OR Apache-2.0"
2727

2828
[workspace.dependencies]
29+
alloy = { version = "0.5.4", features = ["dyn-abi", "json-abi"] }
2930
anyhow = "1.0"
3031
async-graphql = { version = "7.0.11", features = ["chrono", "uuid"] }
3132
async-graphql-axum = "7.0.11"

chain/ethereum/src/adapter.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,12 @@ pub enum EthereumRpcError {
104104

105105
#[derive(Error, Debug)]
106106
pub enum ContractCallError {
107-
#[error("ABI error: {0}")]
108-
ABIError(#[from] ABIError),
109-
/// `Token` is not of expected `ParamType`
110-
#[error("type mismatch, token {0:?} is not of kind {1:?}")]
111-
TypeError(Token, ParamType),
112-
#[error("error encoding input call data: {0}")]
113-
EncodingError(ethabi::Error),
107+
#[error("ABI error: {0:#}")]
108+
ABIError(anyhow::Error),
109+
#[error("type mismatch, decoded value {0:?} is not of kind {1:?}")]
110+
TypeError(graph::abi::DynSolValue, graph::abi::DynSolType),
111+
#[error("error encoding input call data: {0:#}")]
112+
EncodingError(anyhow::Error),
114113
#[error("call error: {0}")]
115114
Web3Error(web3::Error),
116115
#[error("ethereum node took too long to perform call")]
@@ -1165,7 +1164,7 @@ pub trait EthereumAdapter: Send + Sync + 'static {
11651164
logger: &Logger,
11661165
call: &ContractCall,
11671166
cache: Arc<dyn EthereumCallCache>,
1168-
) -> Result<(Option<Vec<Token>>, call::Source), ContractCallError>;
1167+
) -> Result<(Option<Vec<graph::abi::DynSolValue>>, call::Source), ContractCallError>;
11691168

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

11801179
fn get_balance(
11811180
&self,
@@ -1204,9 +1203,9 @@ mod tests {
12041203
use graph::blockchain::TriggerFilter as _;
12051204
use graph::firehose::{CallToFilter, CombinedFilter, LogFilter, MultiLogFilter};
12061205
use graph::petgraph::graphmap::GraphMap;
1207-
use graph::prelude::ethabi::ethereum_types::H256;
12081206
use graph::prelude::web3::types::Address;
12091207
use graph::prelude::web3::types::Bytes;
1208+
use graph::prelude::web3::types::H256;
12101209
use graph::prelude::EthereumCall;
12111210
use hex::ToHex;
12121211
use itertools::Itertools;

chain/ethereum/src/data_source.rs

Lines changed: 35 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use anyhow::{anyhow, Error};
22
use anyhow::{ensure, Context};
3+
use graph::abi::EventExt;
4+
use graph::abi::FunctionExt;
35
use graph::blockchain::{BlockPtr, TriggerWithHandler};
46
use graph::components::metrics::subgraph::SubgraphInstanceMetrics;
57
use graph::components::store::{EthereumCallCache, StoredDynamicDataSource};
@@ -15,6 +17,10 @@ use graph::futures03::stream::FuturesOrdered;
1517
use graph::futures03::TryStreamExt;
1618
use graph::prelude::ethabi::ethereum_types::H160;
1719
use graph::prelude::ethabi::StateMutability;
20+
use graph::prelude::lazy_static;
21+
use graph::prelude::regex::Regex;
22+
use graph::prelude::web3::types::Address;
23+
use graph::prelude::web3::types::H160;
1824
use graph::prelude::{Link, SubgraphManifestValidationError};
1925
use graph::slog::{debug, error, o, trace};
2026
use itertools::Itertools;
@@ -33,6 +39,7 @@ use graph::{
3339
async_trait,
3440
ethabi::{Address, Event, Function, LogParam, ParamType, RawLog},
3541
serde_json, warn,
42+
async_trait, serde_json, warn,
3643
web3::types::{Log, Transaction, H256},
3744
BlockNumber, CheapClone, EthereumCall, LightEthereumBlock, LightEthereumBlockExt,
3845
LinkResolver, Logger,
@@ -525,28 +532,28 @@ impl DataSource {
525532
}
526533
}
527534

528-
/// Returns the contract event with the given signature, if it exists. A an event from the ABI
535+
/// Returns the contract event with the given signature, if it exists. An event from the ABI
529536
/// will be matched if:
530537
/// 1. An event signature is equal to `signature`.
531538
/// 2. There are no equal matches, but there is exactly one event that equals `signature` if all
532539
/// `indexed` modifiers are removed from the parameters.
533-
fn contract_event_with_signature(&self, signature: &str) -> Option<&Event> {
540+
fn contract_event_with_signature(&self, signature: &str) -> Option<&graph::abi::Event> {
534541
// Returns an `Event(uint256,address)` signature for an event, without `indexed` hints.
535-
fn ambiguous_event_signature(event: &Event) -> String {
542+
fn ambiguous_event_signature(event: &graph::abi::Event) -> String {
536543
format!(
537544
"{}({})",
538545
event.name,
539546
event
540547
.inputs
541548
.iter()
542-
.map(|input| event_param_type_signature(&input.kind))
549+
.map(|input| input.selector_type().into_owned())
543550
.collect::<Vec<_>>()
544551
.join(",")
545552
)
546553
}
547554

548555
// Returns an `Event(indexed uint256,address)` type signature for an event.
549-
fn event_signature(event: &Event) -> String {
556+
fn event_signature(event: &graph::abi::Event) -> String {
550557
format!(
551558
"{}({})",
552559
event.name,
@@ -556,40 +563,13 @@ impl DataSource {
556563
.map(|input| format!(
557564
"{}{}",
558565
if input.indexed { "indexed " } else { "" },
559-
event_param_type_signature(&input.kind)
566+
input.selector_type()
560567
))
561568
.collect::<Vec<_>>()
562569
.join(",")
563570
)
564571
}
565572

566-
// Returns the signature of an event parameter type (e.g. `uint256`).
567-
fn event_param_type_signature(kind: &ParamType) -> String {
568-
use ParamType::*;
569-
570-
match kind {
571-
Address => "address".into(),
572-
Bytes => "bytes".into(),
573-
Int(size) => format!("int{}", size),
574-
Uint(size) => format!("uint{}", size),
575-
Bool => "bool".into(),
576-
String => "string".into(),
577-
Array(inner) => format!("{}[]", event_param_type_signature(inner)),
578-
FixedBytes(size) => format!("bytes{}", size),
579-
FixedArray(inner, size) => {
580-
format!("{}[{}]", event_param_type_signature(inner), size)
581-
}
582-
Tuple(components) => format!(
583-
"({})",
584-
components
585-
.iter()
586-
.map(event_param_type_signature)
587-
.collect::<Vec<_>>()
588-
.join(",")
589-
),
590-
}
591-
}
592-
593573
self.contract_abi
594574
.contract
595575
.events()
@@ -628,7 +608,12 @@ impl DataSource {
628608
})
629609
}
630610

631-
fn contract_function_with_signature(&self, target_signature: &str) -> Option<&Function> {
611+
fn contract_function_with_signature(
612+
&self,
613+
target_signature: &str,
614+
) -> Option<&graph::abi::Function> {
615+
use graph::abi::StateMutability;
616+
632617
self.contract_abi
633618
.contract
634619
.functions()
@@ -642,7 +627,7 @@ impl DataSource {
642627
let mut arguments = function
643628
.inputs
644629
.iter()
645-
.map(|input| format!("{}", input.kind))
630+
.map(|input| format!("{}", input.selector_type()))
646631
.collect::<Vec<String>>()
647632
.join(",");
648633
// `address,uint256,bool)
@@ -732,11 +717,7 @@ impl DataSource {
732717
.into_iter()
733718
.filter_map(|(event_handler, event_abi)| {
734719
event_abi
735-
.parse_log(RawLog {
736-
topics: log.topics.clone(),
737-
data: log.data.clone().0,
738-
})
739-
.map(|log| log.params)
720+
.decode_log(&log)
740721
.map_err(|e| {
741722
trace!(
742723
logger,
@@ -841,20 +822,15 @@ impl DataSource {
841822
)
842823
})?;
843824

844-
// Parse the inputs
845-
//
846-
// Take the input for the call, chop off the first 4 bytes, then call
847-
// `function.decode_input` to get a vector of `Token`s. Match the `Token`s
848-
// with the `Param`s in `function.inputs` to create a `Vec<LogParam>`.
849-
let tokens = match function_abi.decode_input(&call.input.0[4..]).with_context(
850-
|| {
825+
let values = match function_abi
826+
.abi_decode_input(&call.input.0[4..])
827+
.with_context(|| {
851828
format!(
852829
"Generating function inputs for the call {:?} failed, raw input: {}",
853830
&function_abi,
854831
hex::encode(&call.input.0)
855832
)
856-
},
857-
) {
833+
}) {
858834
Ok(val) => val,
859835
// See also 280b0108-a96e-4738-bb37-60ce11eeb5bf
860836
Err(err) => {
@@ -864,27 +840,22 @@ impl DataSource {
864840
};
865841

866842
ensure!(
867-
tokens.len() == function_abi.inputs.len(),
843+
values.len() == function_abi.inputs.len(),
868844
"Number of arguments in call does not match \
869845
number of inputs in function signature."
870846
);
871847

872-
let inputs = tokens
848+
let inputs = values
873849
.into_iter()
874850
.enumerate()
875-
.map(|(i, token)| LogParam {
851+
.map(|(i, value)| graph::abi::DynSolParam {
876852
name: function_abi.inputs[i].name.clone(),
877-
value: token,
853+
value,
878854
})
879855
.collect::<Vec<_>>();
880856

881-
// Parse the outputs
882-
//
883-
// Take the output for the call, then call `function.decode_output` to
884-
// get a vector of `Token`s. Match the `Token`s with the `Param`s in
885-
// `function.outputs` to create a `Vec<LogParam>`.
886-
let tokens = function_abi
887-
.decode_output(&call.output.0)
857+
let values = function_abi
858+
.abi_decode_output(&call.output.0)
888859
.with_context(|| {
889860
format!(
890861
"Decoding function outputs for the call {:?} failed, raw output: {}",
@@ -894,17 +865,17 @@ impl DataSource {
894865
})?;
895866

896867
ensure!(
897-
tokens.len() == function_abi.outputs.len(),
868+
values.len() == function_abi.outputs.len(),
898869
"Number of parameters in the call output does not match \
899870
number of outputs in the function signature."
900871
);
901872

902-
let outputs = tokens
873+
let outputs = values
903874
.into_iter()
904875
.enumerate()
905-
.map(|(i, token)| LogParam {
876+
.map(|(i, value)| graph::abi::DynSolParam {
906877
name: function_abi.outputs[i].name.clone(),
907-
value: token,
878+
value,
908879
})
909880
.collect::<Vec<_>>();
910881

chain/ethereum/src/ethereum_adapter.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use futures03::{future::BoxFuture, stream::FuturesUnordered};
2+
use graph::abi::DynSolValueExt;
3+
use graph::abi::FunctionExt;
24
use graph::blockchain::client::ChainClient;
35
use graph::blockchain::BlockHash;
46
use graph::blockchain::ChainIdentifier;
@@ -17,8 +19,6 @@ use graph::futures03::future::try_join_all;
1719
use graph::futures03::{
1820
self, compat::Future01CompatExt, FutureExt, StreamExt, TryFutureExt, TryStreamExt,
1921
};
20-
use graph::prelude::ethabi::ParamType;
21-
use graph::prelude::ethabi::Token;
2222
use graph::prelude::tokio::try_join;
2323
use graph::prelude::web3::types::U256;
2424
use graph::slog::o;
@@ -28,8 +28,7 @@ use graph::{
2828
blockchain::{block_stream::BlockWithTriggers, BlockPtr, IngestorError},
2929
prelude::{
3030
anyhow::{self, anyhow, bail, ensure, Context},
31-
async_trait, debug, error, ethabi, hex, info, retry, serde_json as json, tiny_keccak,
32-
trace, warn,
31+
async_trait, debug, error, hex, info, retry, serde_json as json, tiny_keccak, trace, warn,
3332
web3::{
3433
self,
3534
types::{
@@ -660,9 +659,10 @@ impl EthereumAdapter {
660659

661660
match bytes.len() >= 4 && &bytes[..4] == solidity_revert_function_selector {
662661
false => None,
663-
true => ethabi::decode(&[ParamType::String], &bytes[4..])
662+
true => graph::abi::DynSolType::String
663+
.abi_decode(&bytes[4..])
664664
.ok()
665-
.and_then(|tokens| tokens[0].clone().into_string()),
665+
.and_then(|val| val.clone().as_str().map(ToOwned::to_owned)),
666666
}
667667
};
668668

@@ -1563,7 +1563,7 @@ impl EthereumAdapterTrait for EthereumAdapter {
15631563
logger: &Logger,
15641564
inp_call: &ContractCall,
15651565
cache: Arc<dyn EthereumCallCache>,
1566-
) -> Result<(Option<Vec<Token>>, call::Source), ContractCallError> {
1566+
) -> Result<(Option<Vec<graph::abi::DynSolValue>>, call::Source), ContractCallError> {
15671567
let mut result = self.contract_calls(logger, &[inp_call], cache).await?;
15681568
// unwrap: self.contract_calls returns as many results as there were calls
15691569
Ok(result.pop().unwrap())
@@ -1574,29 +1574,35 @@ impl EthereumAdapterTrait for EthereumAdapter {
15741574
logger: &Logger,
15751575
calls: &[&ContractCall],
15761576
cache: Arc<dyn EthereumCallCache>,
1577-
) -> Result<Vec<(Option<Vec<Token>>, call::Source)>, ContractCallError> {
1577+
) -> Result<Vec<(Option<Vec<graph::abi::DynSolValue>>, call::Source)>, ContractCallError> {
15781578
fn as_req(
15791579
logger: &Logger,
15801580
call: &ContractCall,
15811581
index: u32,
15821582
) -> Result<call::Request, ContractCallError> {
15831583
// Emit custom error for type mismatches.
1584-
for (token, kind) in call
1584+
for (val, kind) in call
15851585
.args
15861586
.iter()
1587-
.zip(call.function.inputs.iter().map(|p| &p.kind))
1587+
.zip(call.function.inputs.iter().map(|p| p.selector_type()))
15881588
{
1589-
if !token.type_check(kind) {
1590-
return Err(ContractCallError::TypeError(token.clone(), kind.clone()));
1589+
let kind: graph::abi::DynSolType = kind.parse().map_err(|err| {
1590+
ContractCallError::ABIError(anyhow!(
1591+
"failed to parse function input type '{kind}': {err}"
1592+
))
1593+
})?;
1594+
1595+
if !val.type_check(&kind) {
1596+
return Err(ContractCallError::TypeError(val.clone(), kind.clone()));
15911597
}
15921598
}
15931599

15941600
// Encode the call parameters according to the ABI
15951601
let req = {
15961602
let encoded_call = call
15971603
.function
1598-
.encode_input(&call.args)
1599-
.map_err(ContractCallError::EncodingError)?;
1604+
.abi_encode_input(&call.args)
1605+
.map_err(|err| ContractCallError::EncodingError(err.into()))?;
16001606
call::Request::new(call.address, encoded_call, index)
16011607
};
16021608

@@ -1614,15 +1620,15 @@ impl EthereumAdapterTrait for EthereumAdapter {
16141620
logger: &Logger,
16151621
resp: call::Response,
16161622
call: &ContractCall,
1617-
) -> (Option<Vec<Token>>, call::Source) {
1623+
) -> (Option<Vec<graph::abi::DynSolValue>>, call::Source) {
16181624
let call::Response {
16191625
retval,
16201626
source,
16211627
req: _,
16221628
} = resp;
16231629
use call::Retval::*;
16241630
match retval {
1625-
Value(output) => match call.function.decode_output(&output) {
1631+
Value(output) => match call.function.abi_decode_output(&output) {
16261632
Ok(tokens) => (Some(tokens), source),
16271633
Err(e) => {
16281634
// Decode failures are reverts. The reasoning is that if Solidity fails to
@@ -2677,9 +2683,9 @@ mod tests {
26772683
EthereumBlockWithCalls,
26782684
};
26792685
use graph::blockchain::BlockPtr;
2680-
use graph::prelude::ethabi::ethereum_types::U64;
26812686
use graph::prelude::tokio::{self};
26822687
use graph::prelude::web3::transports::test::TestTransport;
2688+
use graph::prelude::web3::types::U64;
26832689
use graph::prelude::web3::types::{Address, Block, Bytes, H256};
26842690
use graph::prelude::web3::Web3;
26852691
use graph::prelude::EthereumCall;

0 commit comments

Comments
 (0)