Skip to content

Commit 6113d2b

Browse files
committed
graph: Handle duplicate constructors in ABIs
Extends normalize_abi_json() to also remove duplicate constructors from ABIs. Some non-compliant ABIs contain multiple constructor entries (e.g., DolomiteMargin ABI has two constructors, likely from incorrectly merged contract ABIs). Alloy's JsonAbi only allows one constructor and fails with 'duplicate field self.constructor' when encountering duplicates. ethabi's Contract type silently handled this by only storing one constructor (the last one encountered during deserialization). The fix keeps only the first constructor and removes any subsequent ones, matching the Solidity spec that a contract can only have one constructor. Fixes subgraph QmacPbft3reGGGL4VBzrZCKHeLpRgU9X2wUJjvPBVweyRV deployment.
1 parent 6d286eb commit 6113d2b

File tree

1 file changed

+56
-1
lines changed

1 file changed

+56
-1
lines changed

graph/src/data_source/common.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ fn normalize_abi_json(json_bytes: &[u8]) -> Result<Vec<u8>, anyhow::Error> {
2828
let mut value: serde_json::Value = serde_json::from_slice(json_bytes)?;
2929

3030
if let Some(array) = value.as_array_mut() {
31-
for item in array {
31+
let mut found_constructor = false;
32+
let mut indices_to_remove = Vec::new();
33+
34+
for (index, item) in array.iter_mut().enumerate() {
3235
if let Some(obj) = item.as_object_mut() {
3336
if let Some(state_mutability) = obj.get_mut("stateMutability") {
3437
if let Some(s) = state_mutability.as_str() {
@@ -37,8 +40,22 @@ fn normalize_abi_json(json_bytes: &[u8]) -> Result<Vec<u8>, anyhow::Error> {
3740
}
3841
}
3942
}
43+
44+
if let Some(item_type) = obj.get("type") {
45+
if item_type == "constructor" {
46+
if found_constructor {
47+
indices_to_remove.push(index);
48+
} else {
49+
found_constructor = true;
50+
}
51+
}
52+
}
4053
}
4154
}
55+
56+
for index in indices_to_remove.iter().rev() {
57+
array.remove(*index);
58+
}
4259
}
4360

4461
Ok(serde_json::to_vec(&value)?)
@@ -2159,6 +2176,44 @@ mod tests {
21592176
assert_eq!(json_abi.len(), 2);
21602177
}
21612178

2179+
#[test]
2180+
fn test_normalize_abi_json_with_duplicate_constructors() {
2181+
let abi_with_duplicate_constructors = r#"[
2182+
{
2183+
"type": "constructor",
2184+
"inputs": [{"name": "param1", "type": "address"}],
2185+
"stateMutability": "nonpayable"
2186+
},
2187+
{
2188+
"type": "function",
2189+
"name": "someFunction",
2190+
"inputs": [],
2191+
"outputs": [],
2192+
"stateMutability": "view"
2193+
},
2194+
{
2195+
"type": "constructor",
2196+
"inputs": [{"name": "param2", "type": "uint256"}],
2197+
"stateMutability": "nonpayable"
2198+
}
2199+
]"#;
2200+
2201+
let normalized = normalize_abi_json(abi_with_duplicate_constructors.as_bytes()).unwrap();
2202+
let result: serde_json::Value = serde_json::from_slice(&normalized).unwrap();
2203+
2204+
if let Some(array) = result.as_array() {
2205+
assert_eq!(array.len(), 2);
2206+
assert_eq!(array[0]["type"], "constructor");
2207+
assert_eq!(array[0]["inputs"][0]["name"], "param1");
2208+
assert_eq!(array[1]["type"], "function");
2209+
} else {
2210+
panic!("Expected JSON array");
2211+
}
2212+
2213+
let json_abi: abi::JsonAbi = serde_json::from_slice(&normalized).unwrap();
2214+
assert_eq!(json_abi.len(), 2);
2215+
}
2216+
21622217
// Helper function to create consistent test ABI
21632218
fn create_test_mapping_abi() -> AbiJson {
21642219
const ABI_JSON: &str = r#"[

0 commit comments

Comments
 (0)