Skip to content

Commit 751ba06

Browse files
committed
gnd: Cast fixed-size address arrays to Bytes[] in scaffold
A non-indexed `address[N]` resolves to `FixedArray(Address, N)` and now maps to a `[Bytes!]` schema field, but the mapping left `entity.x = event.params.x` uncast, assigning `Array<Address>` to a `Bytes[]` field and failing the build. Match `FixedArray(Address, _)` alongside `Array(Address)` so the `changetype` covers both. Safe because an indexed address array is already reduced to a `bytes32` hash leaf before it reaches this cast.
1 parent dd89ae5 commit 751ba06

1 file changed

Lines changed: 26 additions & 3 deletions

File tree

gnd/src/scaffold/mapping.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,25 @@ fn generate_single_handler(resolved: &ResolvedEvent) -> String {
236236
)
237237
}
238238

239-
/// Whether a leaf is an `address[]`, whose binding type `Array<Address>` needs a
240-
/// `changetype` to fit a `Bytes[]` entity field.
239+
/// Whether a leaf is an address array (`address[]` or `address[N]`), whose
240+
/// binding type `Array<Address>` needs a `changetype` to fit a `Bytes[]` entity
241+
/// field.
241242
///
242243
/// Only `address` qualifies: `Address extends Bytes`, so the cast is an upcast.
243244
/// `ethereum.Tuple` extends `Array<Value>` and is not a `Bytes`, so casting a
244245
/// `tuple[]` would be an unchecked reinterpret that compiles and then writes
245246
/// heap pointers into the entity. It has no `Bytes[]` form, so it gets no cast
246247
/// and fails to compile instead.
248+
///
249+
/// An indexed address array reaches the entity as a `bytes32` hash leaf, not an
250+
/// array (`flatten_event_inputs` short-circuits it), so it never gets here and
251+
/// this cast cannot reinterpret a hash.
247252
fn needs_bytes_array_cast(ty: &DynSolType) -> bool {
248-
matches!(ty, DynSolType::Array(inner) if matches!(**inner, DynSolType::Address))
253+
matches!(
254+
ty,
255+
DynSolType::Array(inner) | DynSolType::FixedArray(inner, _)
256+
if matches!(**inner, DynSolType::Address)
257+
)
249258
}
250259

251260
/// Extract callable functions from ABI for documentation comments.
@@ -524,6 +533,20 @@ mod tests {
524533
);
525534
}
526535

536+
#[test]
537+
fn test_needs_bytes_array_cast() {
538+
let cast = |t: &str| needs_bytes_array_cast(&t.parse::<DynSolType>().unwrap());
539+
// Both dynamic and fixed-size address arrays need the upcast to Bytes[].
540+
assert!(cast("address[]"));
541+
assert!(cast("address[3]"));
542+
// Nothing else does: a plain address, a non-address array, or a tuple
543+
// array (which has no Bytes[] form and must fail to compile instead).
544+
assert!(!cast("address"));
545+
assert!(!cast("uint256[]"));
546+
assert!(!cast("uint256[3]"));
547+
assert!(!cast("(address,uint256)[]"));
548+
}
549+
527550
#[test]
528551
fn test_generate_mapping_indexed_reference_params_are_hashes() {
529552
// Indexed reference types are keccak'd into a topic, so the binding

0 commit comments

Comments
 (0)