Skip to content

Commit 09ba026

Browse files
authored
Merge branch 'main' into fix/overloaded-event-enum-derives
2 parents 3f0e7ca + 3b31e6c commit 09ba026

12 files changed

Lines changed: 102 additions & 21 deletions

File tree

.github/dependabot.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
version: 2
22
updates:
3-
- package-ecosystem: "github-actions"
4-
directory: "/"
3+
- package-ecosystem: github-actions
4+
directory: /
55
schedule:
6-
interval: "weekly"
6+
interval: weekly
7+
day: monday
8+
time: "09:00"
9+
timezone: UTC
710
cooldown:
811
default-days: 7
12+
groups:
13+
ci-weekly:
14+
patterns:
15+
- '*'

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ schemars = { version = "1", default-features = false }
6060
rkyv = "0.8"
6161

6262
# macros
63-
proc-macro-error2 = ">=2.0.0, <=2.0.1" # We directly use the `entry_point` private API.
63+
proc-macro-error3 = ">=3.0.0, <=3.0.1" # We directly use the `entry_point` private API.
6464
proc-macro2 = "1.0"
6565
quote = "1.0"
6666
syn = "2.0"

crates/dyn-abi/src/dynamic/event.rs

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,29 @@ impl DynSolEvent {
7373
}
7474
}
7575

76+
let not_anonymous = !self.is_anonymous() as usize;
7677
let indexed = self
7778
.indexed
7879
.iter()
79-
.zip(topics.by_ref().take(self.indexed.len()))
80-
.map(|(ty, topic)| {
81-
let value = ty.decode_event_topic(topic);
82-
Ok(value)
80+
.enumerate()
81+
.map(|(i, ty)| match topics.next() {
82+
Some(topic) => Ok(ty.decode_event_topic(topic)),
83+
// Ran out of topics: report the count we actually have (event hash + `i`
84+
// indexed topics consumed so far) rather than letting the body decode below
85+
// fail with a confusing `Overrun`.
86+
None => Err(Error::TopicLengthMismatch {
87+
expected: num_topics,
88+
actual: not_anonymous + i,
89+
}),
8390
})
8491
.collect::<Result<_>>()?;
8592

86-
let body = self.body.abi_decode_sequence(data)?.into_fixed_seq().expect("body is a tuple");
87-
93+
// Validate the topic count before decoding the body. A log whose topic count does not
94+
// match this event (e.g. an ERC-721 `Transfer` decoded against the ERC-20 `Transfer`
95+
// ABI, which shares the same `topic0`) must surface as `TopicLengthMismatch`, not as an
96+
// `Overrun` from trying to read body words that the extra topics displaced. The early
97+
// `size_hint` check above only fires for exact-size iterators, so this also covers
98+
// iterators (`filter`/`map`/streaming) that don't report an exact length.
8899
let remaining = topics.count();
89100
if remaining > 0 {
90101
return Err(Error::TopicLengthMismatch {
@@ -93,6 +104,8 @@ impl DynSolEvent {
93104
});
94105
}
95106

107+
let body = self.body.abi_decode_sequence(data)?.into_fixed_seq().expect("body is a tuple");
108+
96109
Ok(DecodedEvent { selector: self.topic_0, indexed, body })
97110
}
98111

@@ -215,4 +228,57 @@ mod test {
215228
let encoded = decoded.encode_log_data();
216229
assert_eq!(encoded, log);
217230
}
231+
232+
// ERC-20 and ERC-721 `Transfer` share the same `topic0`, so a topic-only filter matches
233+
// both. Decoding an ERC-721 transfer (4 topics, empty data) against the ERC-20 ABI used to
234+
// fail with a confusing `Overrun` when the topic iterator didn't report an exact size,
235+
// because the body was decoded before the topic count was validated. See alloy#2243.
236+
#[test]
237+
fn topic_count_is_validated_before_body() {
238+
let t0 = b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
239+
// event Transfer(address indexed from, address indexed to, uint256 value)
240+
let event = DynSolEvent {
241+
topic_0: Some(t0),
242+
indexed: vec![DynSolType::Address, DynSolType::Address],
243+
body: DynSolType::Tuple(vec![DynSolType::Uint(256)]),
244+
};
245+
246+
// ERC-721-style log: 4 topics (sig + from + to + tokenId), empty data.
247+
let topics = [
248+
t0,
249+
b256!("0x000000000000000000000000d9a442856c234a39a81a089c06451ebaa4306a72"),
250+
b256!("0x0000000000000000000000002a3dd3eb832af982ec71669e178424b10dca2ede"),
251+
b256!("0x0000000000000000000000000000000000000000000000000000000000000290"),
252+
];
253+
254+
// Exact-size iterator: caught by the up-front `size_hint` check.
255+
let err = event.decode_log_parts(topics.iter().copied(), &[]).unwrap_err();
256+
assert!(
257+
matches!(err, Error::TopicLengthMismatch { expected: 3, actual: 4 }),
258+
"exact iterator: {err:?}"
259+
);
260+
261+
// Non-exact iterator (e.g. `filter`): previously returned `Overrun`.
262+
let err = event.decode_log_parts(topics.iter().copied().filter(|_| true), &[]).unwrap_err();
263+
assert!(
264+
matches!(err, Error::TopicLengthMismatch { expected: 3, actual: 4 }),
265+
"non-exact iterator: {err:?}"
266+
);
267+
}
268+
269+
#[test]
270+
fn too_few_topics_is_a_length_mismatch() {
271+
let t0 = b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
272+
let event = DynSolEvent {
273+
topic_0: Some(t0),
274+
indexed: vec![DynSolType::Address, DynSolType::Address],
275+
body: DynSolType::Tuple(vec![DynSolType::Uint(256)]),
276+
};
277+
278+
// Only sig + one indexed topic, supplied via a non-exact iterator.
279+
let topics =
280+
[t0, b256!("0x000000000000000000000000d9a442856c234a39a81a089c06451ebaa4306a72")];
281+
let err = event.decode_log_parts(topics.iter().copied().filter(|_| true), &[]).unwrap_err();
282+
assert!(matches!(err, Error::TopicLengthMismatch { expected: 3, actual: 2 }), "{err:?}");
283+
}
218284
}

crates/primitives/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ std = [
132132
"indexmap?/std",
133133
"k256?/std",
134134
"secp256k1?/std",
135+
"secp256k1?/global-context",
135136
"keccak-asm?/std",
136137
"proptest?/std",
137138
"rand?/std",

crates/primitives/src/signature/sig.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,15 @@ impl Signature {
407407
let this = self.normalized_s();
408408
let sig = this.to_secp256k1()?;
409409
let msg = secp256k1::Message::from_digest(prehash.0);
410-
let secp = secp256k1::Secp256k1::verification_only();
411-
secp.recover_ecdsa(msg, &sig).map_err(Into::into)
410+
#[cfg(feature = "std")]
411+
{
412+
secp256k1::SECP256K1.recover_ecdsa(msg, &sig).map_err(Into::into)
413+
}
414+
#[cfg(not(feature = "std"))]
415+
{
416+
let secp = secp256k1::Secp256k1::verification_only();
417+
secp.recover_ecdsa(msg, &sig).map_err(Into::into)
418+
}
412419
}
413420

414421
/// Returns the `r` component of this signature.

crates/sol-macro-expander/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ syn = { workspace = true, features = ["extra-traits"] }
3535
heck = "0.5"
3636
hex.workspace = true
3737
indexmap = "2"
38-
proc-macro-error2.workspace = true
38+
proc-macro-error3.workspace = true
3939
sha3.workspace = true
4040

4141
# json

crates/sol-macro-expander/src/expand/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ast::{
77
Spanned, Type, VariableDeclaration, Visit, VisitMut, visit_mut,
88
};
99
use indexmap::IndexMap;
10-
use proc_macro_error2::{abort, emit_error};
10+
use proc_macro_error3::{abort, emit_error};
1111
use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
1212
use quote::{TokenStreamExt, format_ident, quote};
1313
use std::{

crates/sol-macro-expander/src/expand/struct.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ fn expand_encode_type_fns(
207207
Some(Item::Enum(_)) => *ty = Type::Uint(ty.span(), NonZeroU16::new(8)),
208208
Some(Item::Udt(udt)) => *ty = udt.ty.clone(),
209209
Some(item) => {
210-
proc_macro_error2::abort!(item.span(), "Invalid type in struct field: {:?}", item)
210+
proc_macro_error3::abort!(item.span(), "Invalid type in struct field: {:?}", item)
211211
}
212212
}
213213
});

crates/sol-macro-expander/src/expand/ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use super::ExpCtxt;
44
use ast::{Item, Parameters, Spanned, Type, TypeArray};
5-
use proc_macro_error2::{abort, emit_error};
5+
use proc_macro_error3::{abort, emit_error};
66
use proc_macro2::{Ident, Literal, Span, TokenStream};
77
use quote::{ToTokens, quote_spanned};
88
use std::{fmt, num::NonZeroU16};

crates/sol-macro-expander/src/utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,17 @@ impl<T: ToTokens> ToTokens for ExprArray<T> {
8181
}
8282
}
8383

84-
/// Applies [`proc_macro_error2`] programmatically.
84+
/// Applies [`proc_macro_error3`] programmatically.
8585
pub(crate) fn pme_compat(f: impl FnOnce() -> TokenStream) -> TokenStream {
8686
pme_compat_result(|| Ok(f())).unwrap()
8787
}
8888

89-
/// Applies [`proc_macro_error2`] programmatically.
89+
/// Applies [`proc_macro_error3`] programmatically.
9090
pub(crate) fn pme_compat_result(
9191
f: impl FnOnce() -> syn::Result<TokenStream>,
9292
) -> syn::Result<TokenStream> {
9393
let mut r = None;
94-
let e = proc_macro_error2::entry_point(
94+
let e = proc_macro_error3::entry_point(
9595
std::panic::AssertUnwindSafe(|| {
9696
r = Some(f());
9797
Default::default()

0 commit comments

Comments
 (0)