Skip to content

Commit 8d75cdf

Browse files
committed
gnd: Validate event/call handler signatures against ABI
Add validation that event handler event signatures and call handler function signatures match entries in the source ABI. This catches errors like referencing a nonexistent event or using wrong parameter types early, during codegen rather than at deploy time. The matching logic mirrors graph-node's runtime matching: - Event signatures support both indexed and non-indexed forms - Non-indexed fallback only matches if a single event variant exists - Function signatures use canonical form: name(type1,type2,...) Changes: - Add `event` field to EventHandler (Solidity event signature) - Replace `call_handlers: Vec<String>` with `Vec<CallHandler>` struct containing both the function signature and handler name - Add validate_handler_signatures() wired into both codegen and build - Update test ABI fixtures to include actual events/functions
1 parent d12d454 commit 8d75cdf

3 files changed

Lines changed: 586 additions & 18 deletions

File tree

gnd/src/commands/codegen.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,17 @@ type MyEntity @entity(immutable: true) {
657657
"#;
658658
fs::write(project_dir.join("schema.graphql"), schema_content).unwrap();
659659
fs::write(project_dir.join("mapping.ts"), "").unwrap();
660-
fs::write(project_dir.join("TestContract.json"), "[]").unwrap();
660+
let abi = r#"[{
661+
"type": "event",
662+
"name": "Transfer",
663+
"anonymous": false,
664+
"inputs": [
665+
{"name": "from", "type": "address", "indexed": true},
666+
{"name": "to", "type": "address", "indexed": true},
667+
{"name": "value", "type": "uint256", "indexed": false}
668+
]
669+
}]"#;
670+
fs::write(project_dir.join("TestContract.json"), abi).unwrap();
661671

662672
// Run codegen
663673
let opt = CodegenOpt {

gnd/src/manifest.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ pub struct DataSource {
7070
pub source_abi: Option<String>,
7171
/// Event handlers from the mapping.
7272
pub event_handlers: Vec<EventHandler>,
73-
/// Call handler names from the mapping.
74-
pub call_handlers: Vec<String>,
73+
/// Call handlers from the mapping.
74+
pub call_handlers: Vec<CallHandler>,
7575
/// Block handlers from the mapping (with filter info for validation).
7676
pub block_handlers: Vec<BlockHandler>,
7777
}
@@ -96,8 +96,8 @@ pub struct Template {
9696
pub source_abi: Option<String>,
9797
/// Event handlers from the mapping.
9898
pub event_handlers: Vec<EventHandler>,
99-
/// Call handler names from the mapping.
100-
pub call_handlers: Vec<String>,
99+
/// Call handlers from the mapping.
100+
pub call_handlers: Vec<CallHandler>,
101101
/// Block handlers from the mapping (with filter info for validation).
102102
pub block_handlers: Vec<BlockHandler>,
103103
}
@@ -119,13 +119,23 @@ pub struct Abi {
119119
/// An event handler with metadata needed for validation.
120120
#[derive(Debug)]
121121
pub struct EventHandler {
122+
/// The Solidity event signature, e.g. `Transfer(address,address,uint256)`.
123+
pub event: String,
122124
pub handler: String,
123125
/// Whether this handler requires transaction receipts.
124126
pub receipt: bool,
125127
/// Whether this handler has eth call declarations.
126128
pub has_call_decls: bool,
127129
}
128130

131+
/// A call handler with metadata needed for validation.
132+
#[derive(Debug)]
133+
pub struct CallHandler {
134+
/// The Solidity function signature, e.g. `approve(address,uint256)`.
135+
pub function: String,
136+
pub handler: String,
137+
}
138+
129139
/// A block handler with filter info needed for validation.
130140
#[derive(Debug)]
131141
pub struct BlockHandler {
@@ -223,6 +233,7 @@ fn convert_data_source(ds: GraphUnresolvedDS<Chain>) -> DataSource {
223233
.event_handlers
224234
.iter()
225235
.map(|h| EventHandler {
236+
event: h.event.clone(),
226237
handler: h.handler.clone(),
227238
receipt: h.receipt,
228239
has_call_decls: !h.calls.raw_decls.is_empty(),
@@ -232,7 +243,10 @@ fn convert_data_source(ds: GraphUnresolvedDS<Chain>) -> DataSource {
232243
.mapping
233244
.call_handlers
234245
.iter()
235-
.map(|h| h.handler.clone())
246+
.map(|h| CallHandler {
247+
function: h.function.clone(),
248+
handler: h.handler.clone(),
249+
})
236250
.collect(),
237251
block_handlers: eth
238252
.mapping
@@ -320,6 +334,7 @@ fn convert_template(t: GraphUnresolvedDST<Chain>) -> Template {
320334
.event_handlers
321335
.iter()
322336
.map(|h| EventHandler {
337+
event: h.event.clone(),
323338
handler: h.handler.clone(),
324339
receipt: h.receipt,
325340
has_call_decls: !h.calls.raw_decls.is_empty(),
@@ -329,7 +344,10 @@ fn convert_template(t: GraphUnresolvedDST<Chain>) -> Template {
329344
.mapping
330345
.call_handlers
331346
.iter()
332-
.map(|h| h.handler.clone())
347+
.map(|h| CallHandler {
348+
function: h.function.clone(),
349+
handler: h.handler.clone(),
350+
})
333351
.collect(),
334352
block_handlers: eth
335353
.mapping

0 commit comments

Comments
 (0)