Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 4 additions & 106 deletions crates/nexum-runtime/src/host/component/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,13 @@ use std::future::Future;

use alloy_chains::Chain;
use alloy_rpc_types_eth::Filter;
use strum::{EnumString, IntoStaticStr};

use crate::host::provider_pool::{BlockStream, CanonicalLogStream, ProviderError, ProviderPool};

/// The permitted JSON-RPC read surface as a closed type. Methods that
/// sign or mutate node state have no variant, so a guest-supplied
/// signing method (for example `eth_sign` or `eth_sendTransaction`)
/// cannot be represented and never reaches the provider. This is the
/// structural ceiling; an operator allowlist narrows within it and
/// never widens it.
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, IntoStaticStr)]
#[non_exhaustive]
pub enum ChainMethod {
#[strum(serialize = "eth_blockNumber")]
EthBlockNumber,
#[strum(serialize = "eth_call")]
EthCall,
#[strum(serialize = "eth_chainId")]
EthChainId,
#[strum(serialize = "eth_estimateGas")]
EthEstimateGas,
#[strum(serialize = "eth_feeHistory")]
EthFeeHistory,
#[strum(serialize = "eth_gasPrice")]
EthGasPrice,
#[strum(serialize = "eth_maxPriorityFeePerGas")]
EthMaxPriorityFeePerGas,
#[strum(serialize = "eth_getBalance")]
EthGetBalance,
#[strum(serialize = "eth_getBlockByHash")]
EthGetBlockByHash,
#[strum(serialize = "eth_getBlockByNumber")]
EthGetBlockByNumber,
#[strum(serialize = "eth_getBlockReceipts")]
EthGetBlockReceipts,
#[strum(serialize = "eth_getCode")]
EthGetCode,
#[strum(serialize = "eth_getLogs")]
EthGetLogs,
#[strum(serialize = "eth_getProof")]
EthGetProof,
#[strum(serialize = "eth_getStorageAt")]
EthGetStorageAt,
#[strum(serialize = "eth_getTransactionByHash")]
EthGetTransactionByHash,
#[strum(serialize = "eth_getTransactionCount")]
EthGetTransactionCount,
#[strum(serialize = "eth_getTransactionReceipt")]
EthGetTransactionReceipt,
#[strum(serialize = "net_version")]
NetVersion,
}

impl ChainMethod {
/// The wire method name forwarded to the provider. `&'static`
/// because the permitted set is closed, so the name drops straight
/// into alloy's `Cow<'static, str>` method slot without allocating.
pub fn as_str(self) -> &'static str {
self.into()
}
}
/// The read surface is defined once in `nexum-world`; host and guest
/// re-export the same type, so the dispatch table and the guest
/// allowlist cannot drift.
pub use nexum_world::ChainMethod;

/// Async chain backend. Methods mirror [`ProviderPool`] one-to-one;
/// the `impl Future + Send` form bakes in the Send bound generic
Expand Down Expand Up @@ -134,51 +80,3 @@ impl ChainProvider for ProviderPool {
ProviderPool::request(self, chain, method, params_json)
}
}

#[cfg(test)]
mod tests {
use super::ChainMethod;

#[test]
fn read_surface_methods_parse() {
for m in [
"eth_call",
"eth_blockNumber",
"eth_getBalance",
"eth_getLogs",
"eth_getTransactionReceipt",
"net_version",
] {
assert!(ChainMethod::try_from(m).is_ok(), "{m} should parse");
}
}

#[test]
fn signing_and_mutating_methods_have_no_variant() {
for m in [
"eth_sign",
"eth_signTransaction",
"eth_sendTransaction",
"eth_sendRawTransaction",
"eth_accounts",
"personal_sign",
"personal_unlockAccount",
"admin_peers",
"debug_traceCall",
"miner_start",
"eth_notAMethod",
"",
] {
assert!(ChainMethod::try_from(m).is_err(), "{m} must be rejected");
}
}

#[test]
fn as_str_round_trips_the_wire_name() {
assert_eq!(ChainMethod::EthCall.as_str(), "eth_call");
assert_eq!(
ChainMethod::try_from(ChainMethod::EthGetBalance.as_str()).unwrap(),
ChainMethod::EthGetBalance,
);
}
}
6 changes: 4 additions & 2 deletions crates/nexum-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ stderr-echo = []
# calls back into this crate (`bind_host_via_wit_bindgen!`, the host
# trait seam, the tracing facade).
nexum-module-macros = { path = "../nexum-module-macros" }
# The single-source vocabularies: `ChainMethod` (re-exported as
# `chain::ChainMethod`) and the fault labels. Its world-synthesis half
# is unused here, so the guest links `toml` and never calls it.
nexum-world = { path = "../nexum-world" }
alloy-primitives.workspace = true
# Typed EIP-155 chain id; already in the guest graph via alloy-provider.
alloy-chains.workspace = true
Expand Down Expand Up @@ -62,8 +66,6 @@ proptest.workspace = true
# The keeper never touches the orderbook, so a CoW-layer mock would only drag
# the domain crates into this crate's dev graph.
nexum-sdk-test = { path = "../nexum-sdk-test" }
# Pins the strum-derived fault labels to the single-source vocabulary.
nexum-world = { path = "../nexum-world" }

# The wasi:http client only links on the wasm guest target; host-side
# consumers (tests, backtest tooling) compile the `http` module's types
Expand Down
122 changes: 0 additions & 122 deletions crates/nexum-sdk/src/chain/method.rs

This file was deleted.

5 changes: 3 additions & 2 deletions crates/nexum-sdk/src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@

pub mod chainlink;
pub mod eth_call;
pub mod method;
pub mod provider;
pub mod transport;

pub use alloy_chains::Chain;
pub use eth_call::{eth_call_params, parse_eth_call_result};
pub use method::ChainMethod;
/// The read surface is defined once in `nexum-world`; guest and host
/// re-export the same type, so the allowlist cannot drift.
pub use nexum_world::ChainMethod;
pub use provider::{ProviderHost, block_on};
pub use transport::HostTransport;
Loading
Loading