Skip to content

Commit 98bd9d7

Browse files
committed
graph, runtime: Add ethereum.decodeParams host function
ethereum.decode decodes its input as a single ABI value, which fails for calldata/event data whose top-level type is a tuple with a dynamic field (e.g. Gnosis Safe execTransaction): such data is encoded as ABI function parameters and has no leading offset word. Add ethereum.decodeParams, which decodes via abi_decode_params, and gate it behind api version 0.0.10. ethereum.decode is left unchanged. Fixes #5432
1 parent bfe3b9d commit 98bd9d7

5 files changed

Lines changed: 54 additions & 1 deletion

File tree

graph/src/data/subgraph/api_version.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ pub const API_VERSION_0_0_8: Version = Version::new(0, 0, 8);
2424
/// Enables new host function `eth_get_balance`
2525
pub const API_VERSION_0_0_9: Version = Version::new(0, 0, 9);
2626

27+
/// Enables new host function `ethereum.decodeParams`
28+
pub const API_VERSION_0_0_10: Version = Version::new(0, 0, 10);
29+
2730
/// Before this check was introduced, there were already subgraphs in the wild with spec version
2831
/// 0.0.3, due to confusion with the api version. To avoid breaking those, we accept 0.0.3 though it
2932
/// doesn't exist.

graph/src/env/mappings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ pub struct InnerMappingHandlers {
175175
entity_cache_dead_weight: EnvVarBoolean,
176176
#[envconfig(from = "GRAPH_ENTITY_CACHE_SIZE", default = "10000")]
177177
entity_cache_size_in_kb: usize,
178-
#[envconfig(from = "GRAPH_MAX_API_VERSION", default = "0.0.9")]
178+
#[envconfig(from = "GRAPH_MAX_API_VERSION", default = "0.0.10")]
179179
max_api_version: Version,
180180
#[envconfig(from = "GRAPH_MAPPING_HANDLER_TIMEOUT")]
181181
mapping_handler_timeout_in_secs: Option<u64>,

runtime/wasm/src/host_exports.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,30 @@ impl HostExports {
12361236
ty.abi_decode(&data).context("Failed to decode")
12371237
}
12381238

1239+
/// Like [`Self::ethereum_decode`], but decodes `data` as ABI function
1240+
/// parameters (the layout used by transaction calldata and event data)
1241+
/// rather than as a single ABI value. The two differ only for a top-level
1242+
/// tuple with at least one dynamic field: calldata has no leading offset
1243+
/// word, which `abi_decode` would otherwise expect.
1244+
pub(crate) fn ethereum_decode_params(
1245+
&self,
1246+
types: String,
1247+
data: Vec<u8>,
1248+
gas: &GasCounter,
1249+
state: &mut BlockState,
1250+
) -> Result<abi::DynSolValue, anyhow::Error> {
1251+
Self::track_gas_and_ops(
1252+
gas,
1253+
state,
1254+
gas::DEFAULT_GAS_OP.with_args(complexity::Size, &data),
1255+
"ethereum_decode_params",
1256+
)?;
1257+
1258+
let ty: abi::DynSolType = types.parse().context("Failed to read types")?;
1259+
1260+
ty.abi_decode_params(&data).context("Failed to decode")
1261+
}
1262+
12391263
pub(crate) fn yaml_from_bytes(
12401264
&self,
12411265
bytes: &[u8],

runtime/wasm/src/module/context.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,26 @@ impl WasmInstanceContext<'_> {
11801180
}
11811181
}
11821182

1183+
/// function decodeParams(types: String, data: Bytes): ethereum.Value | null
1184+
pub async fn ethereum_decode_params(
1185+
&mut self,
1186+
gas: &GasCounter,
1187+
types_ptr: AscPtr<AscString>,
1188+
data_ptr: AscPtr<Uint8Array>,
1189+
) -> Result<AscPtr<AscEnum<EthereumValueKind>>, HostExportError> {
1190+
let types = asc_get(self, types_ptr, gas)?;
1191+
let data = asc_get(self, data_ptr, gas)?;
1192+
let host_exports = self.as_ref().ctx.host_exports.cheap_clone();
1193+
let ctx = &mut self.as_mut().ctx;
1194+
let result = host_exports.ethereum_decode_params(types, data, gas, &mut ctx.state);
1195+
1196+
// return `null` if it fails
1197+
match result {
1198+
Ok(token) => asc_new(self, &token, gas).await,
1199+
Err(_) => Ok(AscPtr::null()),
1200+
}
1201+
}
1202+
11831203
/// function arweave.transactionData(txId: string): Bytes | null
11841204
pub async fn arweave_transaction_data(
11851205
&self,

runtime/wasm/src/module/instance.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,12 @@ pub(crate) fn build_linker(
462462

463463
link!("ethereum.encode", ethereum_encode, params_ptr);
464464
link!("ethereum.decode", ethereum_decode, params_ptr, data_ptr);
465+
link!(
466+
"ethereum.decodeParams",
467+
ethereum_decode_params,
468+
params_ptr,
469+
data_ptr
470+
);
465471

466472
link!("abort", abort, message_ptr, file_name_ptr, line, column);
467473

0 commit comments

Comments
 (0)