Skip to content

feat(examples): price-alert Chainlink oracle reader (BLEU-846)#18

Open
brunota20 wants to merge 1 commit into
feat/shepherd-sdk-docs-bleu-844from
feat/example-price-alert-bleu-846
Open

feat(examples): price-alert Chainlink oracle reader (BLEU-846)#18
brunota20 wants to merge 1 commit into
feat/shepherd-sdk-docs-bleu-844from
feat/example-price-alert-bleu-846

Conversation

@brunota20

Copy link
Copy Markdown
Collaborator

Summary

First canonical SDK example: `modules/examples/price-alert/`. Polls a Chainlink AggregatorV3 price oracle on every block (throttled by `every_n_blocks`) and emits a Warn-level log when the answer crosses a config-supplied threshold.

What it demonstrates

Pattern Implementation
`chain::request` + ABI decode `sol! interface AggregatorV3 { latestRoundData(...) }` with `abi_decode_returns`
shepherd-sdk helpers `chain::eth_call_params` + `chain::parse_eth_call_result`
Config-driven module `OnceLock` initialised in `init`; read on every event

Module-internal pieces

  • `Settings` (renamed from `Config` to avoid clashing with the wit-bindgen `Config` type alias for the `init` arg).
  • `Direction { Above, Below }` + `classify(answer, threshold, direction)` — pure helper, 2 edge tests at the threshold boundary.
  • `scale_threshold(decimal, decimals)` — hand-rolled because alloy lacks a `Decimal::parse_units` helper. Handles sign, missing decimal point, short/long fractional, rejects garbage. 5 unit tests.
  • `parse_config(entries)` returns `Result<Settings, String>` with human-readable errors. 4 unit tests (happy path, defaults, unknown direction, missing key).

module.toml

`capabilities = ["logging", "chain"]` (no local-store, no cow-api). `[[subscription]]` block on Sepolia. `[config]` defaults to the canonical Sepolia ETH/USD feed (`0x694AA1...5306`) with a 2500.00 USD threshold + "below" direction.

Numbers

  • 11 host unit tests pass
  • clippy clean on host + wasm32-wasip2
  • `.wasm` 206 KB optimised (vs M2 modules at 275–305 KB — smaller because no cow-api / cowprotocol surface)

Stacks on #16 (BLEU-844 SDK docs).

Linear: BLEU-846.

Test plan

  • `cargo test -p price-alert` — 11 host tests.
  • `cargo clippy --target wasm32-wasip2 -p price-alert -- -Dwarnings`.
  • `cargo clippy -p price-alert --tests -- -Dwarnings`.
  • `cargo build --target wasm32-wasip2 --release -p price-alert` — 206 KB.
  • End-to-end against a Sepolia RPC + Chainlink ETH/USD feed — pending the multi-module supervisor (PR feat(ethflow-watcher): decode CoWSwapEthFlow OrderPlacement (BLEU-832) #9 upstream).

New `modules/examples/price-alert/` — first canonical SDK example.
A Shepherd module that polls a Chainlink AggregatorV3 price oracle
on every block (throttled by `every_n_blocks`) and emits a Warn-
level log when the answer crosses a config-supplied threshold.

Demonstrates the three load-bearing patterns of a Shepherd module:

  - `chain::request` + ABI decode via `alloy_sol_types` (sol!
    interface AggregatorV3 declares `latestRoundData`, decode via
    `abi_decode_returns`).
  - shepherd-sdk helpers (`chain::eth_call_params` +
    `chain::parse_eth_call_result`; the SDK's prelude is *not*
    used here because the module needs none of the CoW types).
  - `[config]` driven behaviour parsed once in `init` and stored
    in `OnceLock<Settings>` for read-only access on every event.

Module-internal:

  - `Settings` (renamed from `Config` to avoid clashing with the
    wit-bindgen-generated `Config` type alias for the `init` arg).
  - `Direction { Above, Below }` deciding which side of the
    threshold fires.
  - `scale_threshold(decimal, decimals)` hand-rolled because alloy
    does not ship a `Decimal::parse_units`-style helper; handles
    optional sign, missing decimal point, short / long fractional,
    rejects non-digit garbage. Locked by 5 unit tests.
  - `classify(answer, threshold, direction)` pure 1-liner with 2
    edge tests (at-or-above vs. at-or-below behaviour at the
    boundary).
  - `parse_config(entries)` returns `Result<Settings, String>` with
    human-readable errors; 4 unit tests cover happy path, defaults,
    unknown direction, missing key.

module.toml:

  - `capabilities = ["logging", "chain"]` (no local-store; no
    cow-api).
  - `[[subscription]]` block on Sepolia (chain_id 11155111).
  - `[config]` ships defaults pointing at the canonical Sepolia
    ETH/USD feed with a 2500.00 USD threshold + "below" direction.

11 host tests; clippy clean on host + wasm32-wasip2. .wasm is
206 KB optimised — comparable to the M2 modules (twap 305 KB,
ethflow 275 KB) and dominated by alloy-sol-types + wit-bindgen
runtime.
@linear-code

linear-code Bot commented Jun 17, 2026

Copy link
Copy Markdown

BLEU-846

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant