Skip to content

Commit e6dca00

Browse files
committed
core: Implement struct field access in declarative calls (spec v1.4.0)
This commit implements support for struct field access in declarative calls, allowing subgraphs to access nested struct fields using dot notation. Key changes: - Add struct field access parsing and validation - Support arbitrary nesting depth for field access - Update subgraph manifest documentation - Add comprehensive tests for struct field access functionality - Update API version to support new feature
1 parent 8929c43 commit e6dca00

File tree

7 files changed

+1162
-17
lines changed

7 files changed

+1162
-17
lines changed

chain/ethereum/src/data_source.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use graph::{
4141

4242
use graph::data::subgraph::{
4343
calls_host_fn, DataSourceContext, Source, MIN_SPEC_VERSION, SPEC_VERSION_0_0_8,
44-
SPEC_VERSION_1_2_0,
44+
SPEC_VERSION_1_2_0, SPEC_VERSION_1_4_0,
4545
};
4646

4747
use crate::adapter::EthereumAdapter as _;
@@ -74,6 +74,38 @@ pub struct DataSource {
7474
pub contract_abi: Arc<MappingABI>,
7575
}
7676

77+
/// Checks if a declarative call uses struct field access that would require spec version 1.4.0+
78+
/// This detects if the call was parsed with ABI context to resolve named fields
79+
fn call_uses_named_field_access(call_expr: &graph::data_source::common::CallExpr) -> bool {
80+
// Check address for struct field access
81+
if has_struct_field_access(&call_expr.address) {
82+
return true;
83+
}
84+
85+
// Check all arguments for struct field access
86+
for arg in &call_expr.args {
87+
if has_struct_field_access(arg) {
88+
return true;
89+
}
90+
}
91+
92+
false
93+
}
94+
95+
/// Helper function to check if a CallArg uses struct field access
96+
fn has_struct_field_access(arg: &graph::data_source::common::CallArg) -> bool {
97+
use graph::data_source::common::{CallArg, EthereumArg};
98+
99+
match arg {
100+
CallArg::Ethereum(EthereumArg::StructField(_, indices)) => {
101+
// If we have struct field access with indices, it means named fields were resolved
102+
// This feature requires spec version 1.4.0+
103+
!indices.is_empty()
104+
}
105+
_ => false,
106+
}
107+
}
108+
77109
impl blockchain::DataSource<Chain> for DataSource {
78110
fn from_template_info(
79111
info: InstanceDSTemplateInfo,
@@ -380,6 +412,19 @@ impl blockchain::DataSource<Chain> for DataSource {
380412
}
381413
}
382414

415+
if spec_version < &SPEC_VERSION_1_4_0 {
416+
for handler in &self.mapping.event_handlers {
417+
for call in handler.calls.decls.as_ref() {
418+
if call_uses_named_field_access(&call.expr) {
419+
errors.push(anyhow!(
420+
"handler {}: struct field access by name in declarative calls is only supported for specVersion >= 1.4.0", handler.event
421+
));
422+
break;
423+
}
424+
}
425+
}
426+
}
427+
383428
for handler in &self.mapping.event_handlers {
384429
for call in handler.calls.decls.as_ref() {
385430
match self.mapping.find_abi(&call.expr.abi) {
@@ -800,7 +845,7 @@ impl DataSource {
800845
"transaction" => format!("{}", &transaction.hash),
801846
});
802847
let handler = event_handler.handler.clone();
803-
let calls = DeclaredCall::from_log_trigger(
848+
let calls = DeclaredCall::from_log_trigger_with_event(
804849
&self.mapping,
805850
&event_handler.calls,
806851
&log,

docs/subgraph-manifest.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ The `mapping` field may be one of the following supported mapping manifests:
9898

9999
### 1.5.3 Declaring calls
100100

101-
_Available from spec version 1.2.0_
101+
_Available from spec version 1.2.0. Struct field access available from spec version 1.4.0_
102102

103103
Declared calls are performed in parallel before the handler is run and can
104104
greatly speed up syncing. Mappings access the call results simply by using
@@ -118,7 +118,17 @@ Each call is of the form `<ABI>[<address>].<function>(<args>)`:
118118
| **function** | *String* | The name of a view function in the contract |
119119
| **args** | *[Expr]* | The arguments to pass to the function |
120120

121-
The `Expr` can be either `event.address` or `event.params.<name>`.
121+
#### Expression Types
122+
123+
The `Expr` can be one of the following:
124+
125+
| Expression | Description |
126+
| --- | --- |
127+
| **event.address** | The address of the contract that emitted the event |
128+
| **event.params.&lt;name&gt;** | A simple parameter from the event |
129+
| **event.params.&lt;name&gt;.&lt;index&gt;** | A field from a struct parameter by numeric index |
130+
| **event.params.&lt;name&gt;.&lt;fieldName&gt;** | A field from a struct parameter by field name (spec version 1.4.0+) |
131+
122132

123133
## 1.6 Path
124134
A path has one field `path`, which either refers to a path of a file on the local dev machine or an [IPLD link](https://github.com/ipld/specs/).

graph/src/data/subgraph/api_version.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ pub const SPEC_VERSION_1_2_0: Version = Version::new(1, 2, 0);
6060
// represents the write order across all entity types in the subgraph.
6161
pub const SPEC_VERSION_1_3_0: Version = Version::new(1, 3, 0);
6262

63+
// Enables struct field access in declarative calls
64+
pub const SPEC_VERSION_1_4_0: Version = Version::new(1, 4, 0);
65+
6366
// The latest spec version available
64-
pub const LATEST_VERSION: &Version = &SPEC_VERSION_1_3_0;
67+
pub const LATEST_VERSION: &Version = &SPEC_VERSION_1_4_0;
6568

6669
pub const MIN_SPEC_VERSION: Version = Version::new(0, 0, 2);
6770

0 commit comments

Comments
 (0)