diff --git a/Cargo.toml b/Cargo.toml index d495088..d3dbf44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "crates/nexum-engine", + "crates/shepherd-sdk", "modules/ethflow-watcher", "modules/example", "modules/twap-monitor", diff --git a/crates/shepherd-sdk/Cargo.toml b/crates/shepherd-sdk/Cargo.toml new file mode 100644 index 0000000..85522ab --- /dev/null +++ b/crates/shepherd-sdk/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "shepherd-sdk" +version = "0.1.0" +edition.workspace = true +license.workspace = true +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 +# 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"] } diff --git a/crates/shepherd-sdk/src/lib.rs b/crates/shepherd-sdk/src/lib.rs new file mode 100644 index 0000000..dc815d7 --- /dev/null +++ b/crates/shepherd-sdk/src/lib.rs @@ -0,0 +1,99 @@ +//! # shepherd-sdk +//! +//! Guest-side SDK for Shepherd modules. The crate is the shared +//! companion to the per-module `wit_bindgen::generate!` invocation: +//! modules keep their own wit-bindgen call (which emits the world- +//! specific `Guest` trait, `HostError` shape, and host import shims +//! into the module's own crate) and pull helpers + canonical +//! primitive types from here. +//! +//! ## What lives here +//! +//! - [`prelude`] — `use shepherd_sdk::prelude::*` imports the +//! protocol-level types modules need on every other line: alloy +//! primitives ([`Address`](alloy_primitives::Address), +//! [`B256`](alloy_primitives::B256), +//! [`Bytes`](alloy_primitives::Bytes), +//! [`U256`](alloy_primitives::U256), [`keccak256`]( +//! alloy_primitives::keccak256)) and cowprotocol's order / +//! signing surface ([`OrderCreation`](cowprotocol::OrderCreation), +//! [`OrderData`](cowprotocol::OrderData), +//! [`OrderUid`](cowprotocol::OrderUid), +//! [`OrderKind`](cowprotocol::OrderKind), +//! [`Signature`](cowprotocol::Signature), +//! [`Chain`](cowprotocol::Chain), +//! [`GPv2OrderData`](cowprotocol::GPv2OrderData), +//! [`EMPTY_APP_DATA_JSON`](cowprotocol::EMPTY_APP_DATA_JSON), and +//! the [`ApiError`](cowprotocol::ApiError) + +//! [`OrderPostErrorKind`](cowprotocol::error::OrderPostErrorKind) +//! retry contract). +//! +//! - [`cow`] (BLEU-840) — `GPv2OrderData` <-> `OrderData` bridging, +//! `IConditionalOrder` revert decoding, `RetryAction` classifier. +//! Stubbed in this skeleton; populated by the BLEU-840 extraction. +//! +//! - [`chain`] (BLEU-840) — `eth_call` JSON plumbing +//! (`eth_call_params`, `parse_eth_call_result`, `decode_revert_hex`). +//! Stubbed in this skeleton; populated by the BLEU-840 extraction. +//! +//! - [`store`] (BLEU-840) — `WatchSet` and `BackoffLedger` helpers +//! per ADR-0006. Stubbed in this skeleton. +//! +//! ## Why no `wit_bindgen::generate!` here +//! +//! The macro emits types into the calling crate (the module's +//! cdylib). Re-exporting wit-bindgen output from a library crate +//! would duplicate symbols and break the component-export contract. +//! 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 +//! side, neutral to which world the module exports. + +#![warn(missing_docs)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +pub mod prelude; + +/// CoW Protocol bridging: `GPv2OrderData` <-> typed `OrderData`, +/// `IConditionalOrder` revert decoding, retry classification. +/// +/// Skeleton — populated by [BLEU-840]( +/// https://linear.app/bleu-builders/issue/BLEU-840). +pub mod cow {} + +/// `eth_call` JSON plumbing: build the `[{to, data}, "latest"]` +/// params object, parse the `"0x..."` hex result, decode revert +/// payloads from the host's structured error data. +/// +/// Skeleton — populated by [BLEU-840]( +/// https://linear.app/bleu-builders/issue/BLEU-840). +pub mod chain {} + +/// `local-store` helpers: `WatchSet`, `BackoffLedger` per ADR-0006. +/// +/// Skeleton — populated by [BLEU-840]( +/// https://linear.app/bleu-builders/issue/BLEU-840). +pub mod store {} + +#[cfg(test)] +mod tests { + //! The skeleton has no behaviour to exercise; this test just + //! locks the prelude's surface — the build itself proves the + //! re-exports compile against both `wasm32-wasip2` and the + //! host target. + + use crate::prelude::*; + + #[test] + fn prelude_re_exports_resolve() { + let _addr: Address = Address::ZERO; + let _hash: B256 = B256::ZERO; + let _amt: U256 = U256::ZERO; + let _empty: Bytes = Bytes::new(); + // cowprotocol re-exports + let _kind: OrderKind = OrderKind::Sell; + let _chain: Chain = Chain::Sepolia; + assert_eq!(EMPTY_APP_DATA_JSON, "{}"); + } +} diff --git a/crates/shepherd-sdk/src/prelude.rs b/crates/shepherd-sdk/src/prelude.rs new file mode 100644 index 0000000..75cf3d3 --- /dev/null +++ b/crates/shepherd-sdk/src/prelude.rs @@ -0,0 +1,38 @@ +//! Bulk-imports the protocol primitives every Shepherd module uses on +//! every other line. `use shepherd_sdk::prelude::*` is a one-liner that +//! covers alloy address / hash / numeric types plus cowprotocol's +//! order, signing, and orderbook-error surface. +//! +//! The wit-bindgen-generated types (`Guest`, `HostError`, `Event`, …) +//! are **not** re-exported here because they live in each module's own +//! crate (one `wit_bindgen::generate!` call per cdylib). The prelude +//! covers only the host-neutral protocol layer that the SDK helpers +//! consume by value. + +pub use alloy_primitives::{Address, B256, Bytes, U256, address, b256, hex, keccak256}; + +pub use cowprotocol::{ + // App-data + chain + domain identity. + Chain, + DomainSeparator, + EMPTY_APP_DATA_HASH, + EMPTY_APP_DATA_JSON, + // Settlement primitives carried in event payloads and order bodies. + GPv2OrderData, + // Orderbook submission body + the parts every assembly path touches. + OrderCreation, + OrderData, + OrderKind, + // Order identity. + OrderUid, + SellTokenSource, + BuyTokenDestination, + // Signing. + Signature, + SigningScheme, +}; + +/// 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};