Skip to content

feat(lending-health): add T0 read-only Kamino lending position monitor#110

Open
CECILIA-MULANDI wants to merge 3 commits into
zeroclaw-labs:mainfrom
CECILIA-MULANDI:bounty/lending-health
Open

feat(lending-health): add T0 read-only Kamino lending position monitor#110
CECILIA-MULANDI wants to merge 3 commits into
zeroclaw-labs:mainfrom
CECILIA-MULANDI:bounty/lending-health

Conversation

@CECILIA-MULANDI

Copy link
Copy Markdown

Summary

  • Add lending-health, a T0 read-only tool plugin that monitors a Solana wallet's Kamino Lend positions.
  • Input is a single wallet public key. The plugin discovers every obligation the wallet owns on the configured Kamino market, fetches each position's latest metrics, and returns a per-position green/amber/red report plus an overall alert equal to the worst tier across all positions.
  • Designed for daily install-and-forget use behind a cron SOP: "Every 30 minutes, call lending-health for my Kamino wallet. Page me immediately if the overall alert is amber or red."
  • Zero configuration required. Defaults to Kamino's public API on the primary lending market on mainnet-beta. Operators can override api_base_url, env, market_pubkey, health_amber_bps, and health_red_bps.

Custody tier

T0. No transaction builder, no signer, no wallet secret, no write RPC method. Cannot sign, borrow, repay, deposit, withdraw, or move funds. Cannot hit an arbitrary endpoint: API URL and market pubkey are operator config, not tool arguments. Permissions requested are http_client (two well-known Kamino paths, GET only, both hardcoded in the shim) and config_read (endpoint URL, market pubkey, env, alert thresholds).

Threat model, two prompt-injection transcripts, and defenses (base58 32-byte validation, deny_unknown_fields, obligation echo check, NaN/negative rejection, tag range check) are in the plugin README.

Layout

Follows the redact-text reference exactly:

  • Pure policy core in src/lending_health.rs, zero wasm or HTTP dependency, exercised by 50 host tests via plain cargo test.
  • Thin #[cfg(target_family = "wasm")] shim in src/lib.rs that validates args, discovers obligations, fetches metrics, aggregates, and emits structured logs through the logging import.
  • manifest.toml declares only the two permissions the plugin actually uses.
  • #[serde(deny_unknown_fields)] on ExecuteArgs and additionalProperties: false plus base58 regex on the JSON Schema.

Validation

cd plugins/lending-health
cargo test --locked        # 50 passed
cargo build --locked --target wasm32-wasip2 --release   # 374 KB
  • Host tests cover config defaults, market pubkey validation, API URL rules, env allowlist, threshold bounds and injection, pubkey validation, URL composition, obligations parser, metrics parser (hostile responses, NaN, negatives, tag range), analyzer at each tier boundary, aggregation across positions, prompt-injection rejection, and one end-to-end round trip whose payload was pulled from Kamino's public API on 2026-07-18.
  • Output is shaped for LLM context budget: report is a small JSON object with only the fields the model needs (alert, summary, per-position ratios and health), not raw Kamino API payloads.
  • Plugin installed cleanly through the runtime: zeroclaw plugin install plugins/lending-health accepts the manifest, zeroclaw plugin list shows the plugin, zeroclaw plugin info lending-health reads capabilities and permissions.
  • Live agent invocation not included: blocked on the open ZeroClaw runtime bug in Anthropic credential resolution (zeroclaw#9154). Plugin loading and metadata are unaffected.

wasm32-wasip2 notes

The stock Solana client stack does not compile for wasm32-wasip2. The dependency set is deliberately narrow:

  • waki 0.5.1 for blocking WASI HTTP. Waki vendors its own wit-bindgen 0.34 alongside our 0.46; both coexist. Waki emits wasi:http@0.2.4 imports while the current host baseline is @0.2.6. Both link without issue.
  • serde_json for one hand-shaped GET per API endpoint and defensive response parsers.
  • bs58 0.5 for public-key validation.
  • No solana-program, no solana-client. Nothing else was needed at v0.1.

Kamino returns Decimal fields as JSON strings for precision. The parser accepts either string or number, and rejects NaN, positive infinity, and negatives before any arithmetic. Health calc uses f64 for the ratio then rounds to u32 basis points, so tier comparisons against operator config are bit-exact.

Release component size: about 374 KB with opt-level = "s", lto = true, strip = true, codegen-units = 1.

Security & Privacy Impact

  • New permissions, capabilities, or file system access scope: http_client and config_read only. No filesystem, no sockets, no memory backend.
  • New runtime external network calls: Yes. Two GET paths against api_base_url, both hardcoded in the shim. Method is always GET. Only operator-supplied config controls the host.
  • Secrets, tokens, credentials handling changed: No.
  • PII, real identities, or personal data in diff, tests, fixtures, or docs: No. The wallet address used in the example is public on-chain state.

Compatibility

  • Backward compatible: Yes. New plugin only.
  • Config, env, or CLI surface changed: No changes outside the new plugin directory.
  • No changes to shared registry format or CI.

v0.2 plan

  • Replace the Kamino HTTP layer with on-chain reads via getMultipleAccountsInfo on the obligation and referenced reserves, with hand-rolled borsh decoders. Same policy core and report shape.
  • Multi-market support once Kamino's public API exposes more than one lending market.
  • Optional per-reserve breakdown of deposits and borrows, guarded by a config toggle.

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