Skip to content
Open
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
2 changes: 1 addition & 1 deletion crates/shepherd-sdk-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repository.workspace = true
description = "In-memory host mocks for Shepherd module unit tests. Implements shepherd_sdk::host::{ChainHost, LocalStoreHost, CowApiHost, LoggingHost}."

[lib]
# Plain library, host-only module Cargo.toml lists this under
# Plain library, host-only - module Cargo.toml lists this under
# [dev-dependencies] so it never ships in the wasm bundle.

[dependencies]
Expand Down
5 changes: 3 additions & 2 deletions crates/shepherd-sdk-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@
//! The traits use [`shepherd_sdk::host::HostError`] rather than the
//! `HostError` `wit_bindgen::generate!` emits per-module. A module
//! bridges with two trivial `From` impls (one each direction) on its
//! own crate boundary see the M3 tutorial (BLEU-848) for the exact
//! own crate boundary - see the M3 tutorial (BLEU-848) for the exact
//! shape.

#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![warn(missing_docs)]

use std::cell::RefCell;
Expand Down Expand Up @@ -213,7 +214,7 @@ impl MockLocalStore {
self.rows.borrow().is_empty()
}

/// Direct read for assertions bypasses the trait.
/// Direct read for assertions - bypasses the trait.
pub fn snapshot(&self) -> HashMap<String, Vec<u8>> {
self.rows.borrow().clone()
}
Expand Down
4 changes: 2 additions & 2 deletions crates/shepherd-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ repository.workspace = true
description = "Guest-side SDK for Shepherd modules: re-exports, helpers, and prelude on top of cowprotocol + alloy types."

[lib]
# Plain library modules link this and emit their own cdylib for the
# Plain library - modules link this and emit their own cdylib for the
# WASM Component. Building shepherd-sdk on the host target is also
# supported so the helpers are unit-testable without a wasm toolchain.

[dependencies]
cowprotocol = { version = "1.0.0-alpha.3", default-features = false }
alloy-primitives = { version = "1.6", default-features = false, features = ["std", "serde"] }
alloy-sol-types = { version = "1.5", default-features = false, features = ["std"] }
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", default-features = false, features = ["alloc"] }
thiserror = "2"
2 changes: 1 addition & 1 deletion crates/shepherd-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use shepherd_sdk::chain::{eth_call_params, parse_eth_call_result};
| Module | What it provides |
|---|---|
| `prelude` | One-liner `use ::*` for alloy primitives + cowprotocol order / signing / orderbook surface. |
| `cow::order` | `gpv2_to_order_data` `GPv2OrderData` -> typed `OrderData`. |
| `cow::order` | `gpv2_to_order_data` - `GPv2OrderData` -> typed `OrderData`. |
| `cow::composable` | `sol! IConditionalOrder` errors + `PollOutcome` + `decode_revert`. |
| `cow::error` | `RetryAction` enum + `classify_api_error` + `try_decode_api_error`. |
| `chain::eth_call` | `eth_call_params`, `parse_eth_call_result`, `decode_revert_hex`. |
Expand Down
18 changes: 9 additions & 9 deletions crates/shepherd-sdk/src/cow/composable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ sol! {
/// computed here match what the contract emits.
#[derive(Debug)]
interface IConditionalOrder {
/// `OrderNotValid(string)` the order condition is permanently
/// `OrderNotValid(string)` - the order condition is permanently
/// not met. Watch towers drop.
error OrderNotValid(string reason);
/// `PollTryNextBlock(string)` try again on the next block.
/// `PollTryNextBlock(string)` - try again on the next block.
error PollTryNextBlock(string reason);
/// `PollTryAtBlock(uint256, string)` try at or after the
/// `PollTryAtBlock(uint256, string)` - try at or after the
/// given block number.
error PollTryAtBlock(uint256 blockNumber, string reason);
/// `PollTryAtEpoch(uint256, string)` try at or after the
/// `PollTryAtEpoch(uint256, string)` - try at or after the
/// given Unix timestamp (seconds).
error PollTryAtEpoch(uint256 timestamp, string reason);
/// `PollNever(string)` the conditional order is dead.
/// `PollNever(string)` - the conditional order is dead.
error PollNever(string reason);
}
}
Expand All @@ -40,7 +40,7 @@ sol! {
/// `Ready` carries the materials the submit path needs; the other
/// variants drive the lifecycle handler (BLEU-830).
///
/// `Ready` is intentionally never produced by [`decode_revert`] it
/// `Ready` is intentionally never produced by [`decode_revert`] - it
/// only comes from the successful return path the poll module
/// constructs at the call site.
#[derive(Debug)]
Expand All @@ -56,15 +56,15 @@ pub enum PollOutcome {
/// orderbook prepends `from` before settlement).
signature: Bytes,
},
/// Retry on the very next block typical for time-sliced TWAP
/// Retry on the very next block - typical for time-sliced TWAP
/// schedules and other handlers that re-check on every tick.
TryNextBlock,
/// Retry once block number reaches the embedded value.
TryOnBlock(u64),
/// Retry once the wall clock (Unix seconds, UTC) reaches the
/// embedded value.
TryAtEpoch(u64),
/// Order is dead drop the watch. Aggregates `OrderNotValid` and
/// Order is dead - drop the watch. Aggregates `OrderNotValid` and
/// `PollNever` reverts; the original reason string is dropped
/// because the lifecycle handler does not key off it today.
DontTryAgain,
Expand All @@ -74,7 +74,7 @@ pub enum PollOutcome {
/// [`PollOutcome`].
///
/// Returns `None` when the selector is not one of the five
/// [`IConditionalOrder`] errors including a bare `Error(string)`
/// [`IConditionalOrder`] errors - including a bare `Error(string)`
/// require-revert. Callers should treat that as `TryNextBlock` (the
/// safe default) so a transient RPC blip does not drop a still-valid
/// watch.
Expand Down
4 changes: 2 additions & 2 deletions crates/shepherd-sdk/src/cow/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! the lifecycle layer dispatches on. The orderbook returns a typed
//! [`ApiError`] JSON body on permanent / transient failures; the host
//! forwards that JSON in `host-error.data` (once the chain backend
//! supports it see the ADR follow-up). Until then,
//! supports it - see the ADR follow-up). Until then,
//! [`classify_api_error`] falls back to `TryNextBlock` so a flaky
//! orderbook does not poison still-valid orders.
//!
Expand All @@ -24,7 +24,7 @@ pub enum RetryAction {
/// Leave the watch / placement in place; the next event will
/// re-attempt.
TryNextBlock,
/// Persist `next_attempt = now + seconds`. Reserved no producer
/// Persist `next_attempt = now + seconds`. Reserved - no producer
/// today (kept so the dispatch contract is stable).
#[allow(dead_code)]
Backoff {
Expand Down
2 changes: 1 addition & 1 deletion crates/shepherd-sdk/src/cow/order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use cowprotocol::{
/// the wire as `bytes32` markers (the `keccak256` of the lowercase
/// variant name). This helper hands them off to cowprotocol's
/// `from_contract_bytes` classifiers and returns `None` when the on-
/// chain payload carries a marker the SDK doesn't recognise the
/// chain payload carries a marker the SDK doesn't recognise - the
/// caller skips the order rather than ship a malformed body.
///
/// `receiver = Address::ZERO` is normalised to `None`; `OrderCreation::
Expand Down
19 changes: 10 additions & 9 deletions crates/shepherd-sdk/src/host.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Host traits the seam between strategy logic and the wit-bindgen
//! Host traits - the seam between strategy logic and the wit-bindgen
//! shims a module generates per-cdylib.
//!
//! Each trait mirrors one nexum / shepherd host interface
Expand All @@ -13,7 +13,7 @@
//!
//! `wit_bindgen::generate!` emits a `HostError` struct into each
//! module's own crate, so its identity is per-module. The SDK
//! exposes [`HostError`] (this module) with the same field shape
//! exposes [`HostError`] (this module) with the same field shape -
//! modules wire a one-liner `From` impl between the two so the
//! traits stay world-neutral and the mocks compile without a wasm
//! toolchain. See `shepherd-sdk-test`'s README for the adapter
Expand All @@ -29,9 +29,9 @@ pub enum LogLevel {
Debug,
/// Steady-state events.
Info,
/// Recoverable errors operator notice but no immediate action.
/// Recoverable errors - operator notice but no immediate action.
Warn,
/// Unrecoverable errors operator should investigate.
/// Unrecoverable errors - operator should investigate.
Error,
}

Expand Down Expand Up @@ -59,7 +59,8 @@ pub enum HostErrorKind {
/// SDK-side counterpart to wit-bindgen's `HostError`. Same field shape
/// so a module bridges between the two with a trivial `From` impl on
/// each side.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[error("{domain}: {message} (code={code}, kind={kind:?})")]
pub struct HostError {
/// Short subsystem identifier (`"chain"`, `"local-store"`,
/// `"cow-api"`, `"logging"`).
Expand Down Expand Up @@ -88,15 +89,15 @@ impl HostError {
}
}

/// `nexum:host/chain` raw JSON-RPC dispatch.
/// `nexum:host/chain` - raw JSON-RPC dispatch.
pub trait ChainHost {
/// Execute a JSON-RPC request against the given chain. The host
/// routes to its configured provider; the SDK does not care which
/// transport (HTTP / WebSocket / mock) implements the call.
fn request(&self, chain_id: u64, method: &str, params: &str) -> Result<String, HostError>;
}

/// `nexum:host/local-store` per-module key-value persistence.
/// `nexum:host/local-store` - per-module key-value persistence.
pub trait LocalStoreHost {
/// Fetch a value. `Ok(None)` when the key is absent.
fn get(&self, key: &str) -> Result<Option<Vec<u8>>, HostError>;
Expand All @@ -108,14 +109,14 @@ pub trait LocalStoreHost {
fn list_keys(&self, prefix: &str) -> Result<Vec<String>, HostError>;
}

/// `shepherd:cow/cow-api` orderbook submission path.
/// `shepherd:cow/cow-api` - orderbook submission path.
pub trait CowApiHost {
/// Submit an `OrderCreation` JSON body. The host returns the
/// canonical order UID on success.
fn submit_order(&self, chain_id: u64, body: &[u8]) -> Result<String, HostError>;
}

/// `nexum:host/logging` structured runtime logs.
/// `nexum:host/logging` - structured runtime logs.
pub trait LoggingHost {
/// Emit a log line at the given level.
fn log(&self, level: LogLevel, message: &str);
Expand Down
15 changes: 8 additions & 7 deletions crates/shepherd-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,30 @@
//!
//! ## What lives here
//!
//! - [`prelude`] `use shepherd_sdk::prelude::*` imports alloy
//! - [`prelude`] - `use shepherd_sdk::prelude::*` imports alloy
//! primitives ([`Address`], [`B256`], [`Bytes`], [`U256`],
//! [`keccak256`]) and cowprotocol's order / signing / orderbook
//! surface ([`OrderCreation`], [`OrderData`], [`OrderUid`],
//! [`OrderKind`], [`Signature`], [`Chain`], [`GPv2OrderData`],
//! [`EMPTY_APP_DATA_JSON`], [`ApiError`], [`OrderPostErrorKind`]).
//!
//! - [`cow`] `GPv2OrderData` -> `OrderData` bridging
//! - [`cow`] - `GPv2OrderData` -> `OrderData` bridging
//! ([`gpv2_to_order_data`]), `IConditionalOrder` revert decoding
//! ([`PollOutcome`] + [`decode_revert`]), and the
//! [`RetryAction`] classifier driving submit-failure dispatch.
//!
//! - [`chain`] `eth_call` JSON plumbing
//! - [`chain`] - `eth_call` JSON plumbing
//! ([`eth_call_params`], [`parse_eth_call_result`],
//! [`decode_revert_hex`]).
//!
//! - [`host`] host trait seam ([`Host`] / [`ChainHost`] /
//! - [`host`] - host trait seam ([`Host`] / [`ChainHost`] /
//! [`LocalStoreHost`] / [`CowApiHost`] / [`LoggingHost`]) plus a
//! host-neutral [`HostError`]. Modules that want host-free tests
//! structure their strategy logic against these traits and slot
//! in the `shepherd-sdk-test` mocks. See the host module docs for
//! the wit-bindgen adapter pattern.
//!
//! - `store` placeholder for `WatchSet` / `BackoffLedger`
//! - `store` - placeholder for `WatchSet` / `BackoffLedger`
//! per ADR-0006. Populated when a second strategy module needs
//! the same key conventions.
//!
Expand All @@ -44,7 +44,7 @@
//! Helpers in this SDK therefore take primitive types (`&[u8]`,
//! `Option<&str>`, slices) rather than the per-module `HostError`
//! struct; modules unpack their `HostError` on the way in. Trade-off
//! documented in ADR-0006 / ADR-0007 the SDK stays on the guest
//! documented in ADR-0006 / ADR-0007 - the SDK stays on the guest
//! side, neutral to which world the module exports.
//!
//! [`Address`]: alloy_primitives::Address
Expand Down Expand Up @@ -76,6 +76,7 @@
//! [`LoggingHost`]: host::LoggingHost
//! [`HostError`]: host::HostError

#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

Expand All @@ -88,7 +89,7 @@ pub mod prelude;
#[cfg(test)]
mod tests {
//! The skeleton has no behaviour to exercise; this test just
//! locks the prelude's surface the build itself proves the
//! locks the prelude's surface - the build itself proves the
//! re-exports compile against both `wasm32-wasip2` and the
//! host target.

Expand Down
2 changes: 1 addition & 1 deletion crates/shepherd-sdk/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub use cowprotocol::{
SigningScheme,
};

/// Re-exported `ApiError` typed error surface from the orderbook
/// Re-exported `ApiError` typed error surface from the orderbook -
/// guest-side helpers (BLEU-840) read this back out of host-error JSON
/// to drive the `RetryAction` dispatch.
pub use cowprotocol::error::{ApiError, OrderPostErrorKind};
Loading