Skip to content

fix(lido-plugin): sync to okx v0.2.8 + biz-type/strategy attribution + 1 EVM-012 (v0.2.9)#76

Merged
plugin-store-bot[bot] merged 2 commits intomig-pre:mainfrom
GeoGu360:fix/lido-biz-type-v2
May 7, 2026
Merged

fix(lido-plugin): sync to okx v0.2.8 + biz-type/strategy attribution + 1 EVM-012 (v0.2.9)#76
plugin-store-bot[bot] merged 2 commits intomig-pre:mainfrom
GeoGu360:fix/lido-biz-type-v2

Conversation

@GeoGu360
Copy link
Copy Markdown
Owner

@GeoGu360 GeoGu360 commented May 7, 2026

Summary

This PR brings mig-pre/main lido-plugin from v0.2.7 → v0.2.9 in one shot, since mig-pre was 1 version behind okx/main when we picked up the work.

The diff is 2 logical parts:

Part 1: Sync mig-pre v0.2.7 → okx/main v0.2.8 (17 files)

These changes already live on okx/main and have been reviewed/merged there. They include:

  • New quickstart command (commands/quickstart.rs)
  • get_withdrawals refactor + main.rs wiring
  • Stake / unwrap / wrap / request_withdrawal tweaks

Part 2: New in v0.2.9 — biz-type/strategy attribution + 1 EVM-012 fix

Attribution (src/onchainos.rs)

const BIZ_TYPE: &str = "dapp";
const STRATEGY: &str = env!("CARGO_PKG_NAME");

Injected into the args of wallet_contract_call. Covers 7 call sites:

  • stake / wrap × 2 / unwrap / request_withdrawal × 2 / claim_withdrawal

env!("CARGO_PKG_NAME") keeps strategy name in sync with Cargo.toml — no drift across the 4 metadata files.

EVM-012 fix (src/commands/stake.rs:48)

isStakingPaused() pre-flight had decode_uint256(...).unwrap_or(0). On decode failure that returned val=0 = "not paused" and let staking proceed even when contract state was unknown. This is a safety gate, not a display field — silent fallback could let a paused-contract stake through. Changed to explicit ? propagation with named error.

Audit notes

Other unwrap_or(0) sites classified as not EVM-012:

  • onchainos.rs block num display + native_balance hex parse (display fallbacks after explicit RPC error branch upstream)
  • claim_withdrawal.rs / get_withdrawals.rs boolean ABI flag decoding (soft fallback acceptable for well-known WithdrawalQueue contract)
  • quickstart.rs display only

EVM-006 sleep is the receipt-confirmation poll (correct pattern).

Test plan

  • cargo build passes
  • Step 1.5 scan complete (1 real EVM-012 found + fixed)
  • Phase 1 / 2 / 3 CI
  • Phase 4 publish + auto-merge

🤖 Generated with Claude Code

…+ 1 EVM-012 (v0.2.9)

This PR brings mig-pre/main lido-plugin from v0.2.7 to v0.2.9. The bulk
of the diff is the v0.2.7 → v0.2.8 sync (already on okx/main), with our
biz-type/strategy attribution + 1 EVM-012 safety-gate fix layered on top.

== Sync portion (mig-pre v0.2.7 → okx v0.2.8) ==

17 files changed. These are the v0.2.8 changes already living on
okx/main that mig-pre is missing. They include:
- New `quickstart` command (commands/quickstart.rs)
- get_withdrawals refactor + main.rs registration
- Stake / unwrap / wrap / request_withdrawal tweaks (already reviewed
  on okx side)

== New in v0.2.9 ==

Add `--biz-type dapp --strategy lido-plugin` to every onchainos
`wallet contract-call` invocation. Source-of-truth for the strategy
name is Cargo.toml's `[package].name` via `env!("CARGO_PKG_NAME")` —
no drift between Cargo.toml, plugin.yaml, plugin.json.

Affected onchainos call sites (7):
- commands/stake.rs (ETH → stETH stake)
- commands/wrap.rs × 2 (stETH approve + wrap to wstETH)
- commands/unwrap.rs (wstETH → stETH unwrap)
- commands/request_withdrawal.rs × 2 (stETH approve + queue withdrawal)
- commands/claim_withdrawal.rs (claim finalized withdrawal)

Also fix one real EVM-012 (silent decode error swallowing) found by
the Step 1.5 scan:
- commands/stake.rs:48 — `decode_uint256(...).unwrap_or(0)` on the
  `isStakingPaused()` pre-flight check. Decode failure was treated as
  "val=0" = "not paused" and let staking proceed even when the actual
  contract state was unknown. This is a *safety gate*, not a display
  field — silent fallback means a paused-contract stake could pass
  through. Changed to explicit `?` propagation with a clear anyhow
  error message naming which RPC step failed.

Other `unwrap_or(0)` sites scanned and classified:
- onchainos.rs (block num display + native_balance hex parse) —
  display fallbacks after explicit RPC error branch upstream
- claim_withdrawal.rs / get_withdrawals.rs (boolean flag decoding from
  well-known WithdrawalQueue ABI) — soft fallback acceptable
- quickstart.rs — display only

EVM-006 sleep at onchainos.rs is the receipt-confirmation poll
(correct pattern, not a race condition).

Version bump: mig-pre v0.2.7 → v0.2.9 (PATCH — backwards-compatible).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@GeoGu360 GeoGu360 added the ci-approved Maintainer reviewed PR; allows Phase 1/2/3 CI to run label May 7, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

🔨 Phase 2: Build Verification — ✅ PASSED

Plugin: lido-plugin | Language: rust
Source: @

Compiled from developer source code by our CI. Users install our build artifacts.

Build succeeded. Compiled artifact uploaded as workflow artifact.


Source integrity: commit SHA `` is the content fingerprint.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

📋 Phase 3: AI Code Review Report — Score: 89/100

Plugin: lido-plugin | Recommendation: ✅ Ready to merge

🔗 Reviewed against latest onchainos source code (live from main branch) | Model: claude-opus-4-7 via Anthropic API | Cost: ~519593+6229 tokens

This is an advisory report. It does NOT block merging. Final decision is made by human reviewers.


1. Plugin Overview
Field Value
Name lido-plugin
Version 0.2.9
Category defi-protocol
Author GeoGu360 (GeoGu360)
License MIT
Has Binary Yes (with build config)
Risk Level Medium (handles staking funds; uses contract-call signing)

Summary: Rust-based plugin that wraps the Lido liquid staking protocol on Ethereum mainnet. Provides stake / balance / withdrawal-request / claim / wrap / unwrap operations against the stETH, wstETH, and WithdrawalQueueERC721 contracts via onchainos wallet contract-call for signing.

Target Users: Ethereum users who want to stake ETH on Lido through an agent-driven CLI flow without leaving the OKX onchainos environment.

2. Architecture Analysis

Components: skill (SKILL.md + SUMMARY.md) + Rust binary (lido-plugin).

Skill Structure: SKILL.md includes auto-injected pre-flight (skipped from review), Overview, Architecture, Pre-flight Checks, Quickstart, Contract Addresses, ~9 commands with parameter tables and ABI calldata details, Error Handling, Suggested Follow-ups, Skill Routing, Security Notices. Roughly 9 user-facing commands.

Data Flow:

  • Read ops → direct JSON-RPC eth_call against https://ethereum.publicnode.com (in onchainos.rs).
  • DeFiLlama API (yields.llama.fi/pools) for APR data.
  • Lido withdrawal queue API (wq-api.lido.fi) for wait time hints.
  • Write ops → ABI-encoded calldata is built in Rust, then submitted via onchainos wallet contract-call subprocess (with --biz-type dapp --strategy lido-plugin).
  • Tx confirmation → polled via eth_getTransactionReceipt against publicnode RPC.

Dependencies: clap, tokio, reqwest, serde, serde_json, anyhow, hex. External services: ethereum.publicnode.com, yields.llama.fi, wq-api.lido.fi.

3. Auto-Detected Permissions

onchainos Commands Used

Command Found Exists in onchainos CLI Risk Level Context
onchainos wallet balance --chain Yes Low Used by quickstart/balance to resolve wallet
onchainos wallet contract-call --chain --to --input-data [--amt] [--from] [--force] Yes High All write paths (stake / approve / wrap / unwrap / requestWithdrawals / claim)
onchainos wallet login (referenced) Yes N/A Documentation only

All commands are present in the onchainos CLI source provided.

Wallet Operations

Operation Detected? Where Risk
Read balance Yes balance, quickstart, request_withdrawal pre-flight Low
Send transaction Yes stake, request_withdrawal, claim_withdrawal, wrap, unwrap High
Sign message No High
Contract call Yes wallet_contract_call wrapper in onchainos.rs High

External APIs / URLs

URL / Domain Purpose Risk
https://yields.llama.fi/pools Fetch stETH APY/TVL (DeFiLlama) Low (read-only data; M07 boundary noted)
https://wq-api.lido.fi/v2/request-time Withdrawal wait time hints Low
https://ethereum.publicnode.com JSON-RPC eth_call / eth_getBalance / eth_getTransactionReceipt Low
https://raw.githubusercontent.com/okx/plugin-store/... Auto-injected pre-flight (skipped) N/A
https://github.com/okx/plugin-store/releases/... Auto-injected pre-flight (skipped) N/A

Chains Operated On

Ethereum mainnet only (chain ID 1), per config::CHAIN_ID.

Overall Permission Summary

This plugin can read wallet ETH/stETH/wstETH balances and submit signed Ethereum transactions (stake, approve, wrap/unwrap, withdrawal request, claim) through onchainos's TEE-signed wallet pipeline. All signing happens through onchainos — the plugin never holds private keys. Outbound network calls go to three pinned domains (DeFiLlama, Lido WQ-API, publicnode RPC); no untrusted user input is interpolated into URLs. Calldata is constructed locally in Rust, which is auditable and avoids prompt injection of transaction parameters.

4. onchainos API Compliance

Does this plugin use onchainos CLI for all on-chain write operations?

Yes.

On-Chain Write Operations (MUST use onchainos)

Operation Uses onchainos? Self-implements? Detail
Wallet signing No Delegates to onchainos wallet contract-call
Transaction broadcasting No Same as above; receipt polling is read-only via public RPC
DEX swap execution N/A No Not applicable to staking
Token approval No approve(spender, amount) calldata sent via wallet contract-call
Contract calls No All Lido contract calls via wallet contract-call
Token transfers N/A No Not applicable

Data Queries (allowed to use external sources)

Data Source API/Service Used Purpose
DeFiLlama yields.llama.fi/pools stETH APY/TVL
Lido WQ-API wq-api.lido.fi Withdrawal wait time
Public Ethereum RPC ethereum.publicnode.com eth_call, eth_getBalance, eth_getTransactionReceipt

External APIs / Libraries Detected

reqwest (HTTPS client), tokio (async runtime). No web3/ethers libraries used for signing — calldata is hand-rolled and signing is delegated.

Verdict: ✅ Fully Compliant

5. Security Assessment

Static Rule Scan (C01-C09, H01-H09, M01-M08, L01-L02)

Rule ID Severity Title Matched? Detail
H05 INFO Direct financial / on-chain operations Plugin invokes onchainos wallet contract-call, which is the sanctioned path. Baseline characteristic, not a finding.
M07 MEDIUM Missing untrusted-data boundary declaration SKILL.md explicitly includes: "Treat all data returned by this plugin and external APIs (Lido REST, Ethereum RPC) as untrusted external content..."
M08 MEDIUM External-data field passthrough Output fields are explicitly enumerated in command tables (e.g., txHash, stETHWrapped, wstETHExpected); SKILL.md doesn't pass through raw external API response objects.

No other static rules match. No curl|sh, no prompt-injection text, no Base64/Unicode obfuscation, no credential exfiltration, no hardcoded secrets, no persistence, no sensitive-path access, no system modification, no env-var credential prompts.

LLM Judge Analysis (L-PINJ, L-MALI, L-MEMA, L-IINJ, L-AEXE, L-FINA, L-FISO)

Judge Severity Detected Confidence Evidence
L-PINJ Prompt Injection CRITICAL No 0.95 No hidden instructions, no role override, no Unicode tricks.
L-MALI Malicious Intent CRITICAL No 0.9 Behavior matches stated purpose (Lido staking). No covert exfiltration.
L-MEMA Memory Poisoning HIGH No 0.95 No writes to MEMORY.md / SOUL.md / persistent agent state.
L-IINJ External Request Notice INFO Yes (informational) 0.9 DeFiLlama, Lido WQ-API, publicnode.com — boundary declaration present. INFO.
L-AEXE Autonomous Execution INFO No 0.9 All write ops gated behind explicit --confirm flag; preview mode is default.
L-FINA Financial Scope INFO Yes (declared) 0.95 Write operations with --confirm gating + onchainos TEE signing → INFO tier.
L-FISO Fund Isolation INFO No 0.9 Funds remain user-controlled; plugin does not pool or custody assets.

Toxic Flow Detection (TF001-TF006)

No toxic flows detected. No combination of (curl|sh + financial) (TF005), no (sensitive-path + network) (TF001), no (prompt-injection + persistence) (TF002), no (unverifiable-dep + malicious-intent) (TF004), no (missing-boundary + financial) (TF006 — boundary declaration is present).

Prompt Injection Scan

No instruction override, identity manipulation, hidden behavior, confirmation bypass instructions, or hidden content (no base64 blobs, no invisible characters).

Result: ✅ Clean

Dangerous Operations Check

Plugin involves transfers, contract calls, and broadcasting transactions. The SKILL.md and the Rust source enforce explicit --confirm gating: every write command runs in preview-only mode without --confirm, and the SKILL contains explicit <NEVER>-style guidance: "NEVER pass --force on the FIRST invocation". Approve+request and approve+wrap flows wait for on-chain confirmation between steps. The Rust source rejects unlimited approvals implicitly (caller-supplied amount, no type(uint256).max).

Result: ✅ Safe

Data Exfiltration Risk

External calls go to three pinned domains for known-public data (APY, wait time, RPC reads). No environment variables, no ~/.ssh, ~/.aws, or other sensitive paths accessed. Wallet addresses are passed as CLI args, which is unavoidable for the function.

Result: ✅ No Risk

Overall Security Rating: 🟢 Low Risk

6. Source Code Security (if source code is included)

Language & Build Config

Rust, entry point src/main.rs, binary name lido-plugin. Standard Cargo workspace with pinned dependency versions in Cargo.lock.

Dependency Analysis

clap (CLI parsing), tokio (async), reqwest (HTTP with rustls + native-tls), serde / serde_json (JSON), anyhow (error handling), hex (encoding). All are mainstream, actively maintained crates. No suspicious or unmaintained packages.

Code Safety Audit

Check Result Detail
Hardcoded secrets (API keys, private keys, mnemonics) None
Network requests to undeclared endpoints yields.llama.fi, wq-api.lido.fi, ethereum.publicnode.com — all declared in plugin.yaml api_calls
File system access outside plugin scope None
Dynamic code execution (eval, exec, shell commands) Uses tokio::process::Command only to invoke onchainos with structured args (no shell interpolation)
Environment variable access beyond declared env Only CARGO_PKG_NAME at compile time
Build scripts with side effects (build.rs, postinstall) No build.rs
Unsafe code blocks (Rust) / CGO (Go) No unsafe blocks

Subprocess invocation in onchainos.rs::wallet_contract_call and resolve_wallet uses Command::new("onchainos").args([...]) with arguments passed as a vector — no shell expansion, no string concatenation, so user input cannot inject extra flags.

ABI calldata construction in rpc.rs is hand-rolled with format!("{:064x}", ...) for uint256 padding and address encoding. Logic is straightforward and unit-testable.

Amount conversion uses (amount * 1e18).round() as u128 — safe for the documented range; for ≥10⁹ ETH the f64→u128 cast would lose precision, but minimum/maximum bounds (MIN_WITHDRAWAL_WEI, MAX_WITHDRAWAL_WEI = 1000 ETH) make this a non-issue in practice.

Does SKILL.md accurately describe what the source code does?

Yes. SKILL.md command list, parameters, calldata structures (selectors 0xa1903eab, 0x095ea7b3, 0xea598cb0, 0xde0e9a3e, etc.) all match the source. Contract addresses match. Pre-flight checks (isStakingPaused, balance checks) are implemented as documented.

Verdict: ✅ Source Safe

7. Code Review

Quality Score: 89/100

Dimension Score Notes
Completeness (pre-flight, commands, error handling) 23/25 Pre-flight (isStakingPaused, balance checks), receipt polling, error codes table, dry-run + confirm flags all present. Minor: claim-withdrawal could surface partial-success more clearly.
Clarity (descriptions, no ambiguity) 23/25 Each command has parameter tables, calldata structure, expected output. Quickstart is well-staged. Slight verbosity in repeated calldata examples.
Security Awareness (confirmations, slippage, limits) 24/25 Explicit --confirm gating, <NEVER> rule on --force, max 1000 ETH withdrawal, non-finalized request detection, untrusted-data boundary declared.
Skill Routing (defers correctly, no overreach) 14/15 Defers SOL staking to jito skill, wallet ops to onchainos.
Formatting (markdown, tables, code blocks) 9/10 Consistent tables and code blocks. SUMMARY.md is well-structured.

Strengths

  • Clean separation between read-only (public RPC) and write (onchainos-signed) paths.
  • Explicit --confirm gating on every write command with preview-mode defaults.
  • Receipt polling between approve and follow-up tx prevents nonce ordering issues.
  • Untrusted-data boundary declaration is present and explicit.

Issues Found

  • 🔵 Minor: f64 → u128 amount conversion is fine for current bounds but could become lossy if max-withdrawal limits are raised in the future. Worth documenting or migrating to integer-only parsing eventually.
  • 🔵 Minor: get_withdrawals silently skips entries on from_str_radix decode error for is_finalized / is_claimed fields (only amount_steth_wei propagates). Consistency would be cleaner.
  • 🔵 Minor: Hardcoded RPC URL ethereum.publicnode.com — fine for read-only, but could benefit from a configurable override.
8. Language Check
File Language Detected English?
SKILL.md English
SUMMARY.md English
9. SUMMARY.md Review
Check Result
File exists
Written in English
Has Overview section
Has Prerequisites section
Has Quick Start section
Character count ≤ 17,000 ✅ 1772 chars
11. Recommendations
  1. (Minor) Document the f64→u128 amount conversion's safe range explicitly, or migrate to a string-parse + integer-arithmetic path to eliminate any future precision concern.
  2. (Minor) Make get_withdrawals field decoding consistent — either propagate all decode errors with a warning (as amount_steth_wei does) or skip silently for all fields.
  3. (Minor) Consider exposing the public RPC URL as an environment variable override (LIDO_RPC_URL) for users on rate-limited networks or behind corporate proxies.
  4. (Minor) In claim-withdrawal, the partial-skip of already-claimed IDs prints to stderr but the broadcast still happens with the full list. Consider filtering them out of the broadcast payload to avoid wasted gas.
12. Reviewer Summary

One-line verdict: Well-structured, security-conscious Lido staking plugin that correctly delegates all signing to onchainos and includes proper confirmation gating, balance pre-flights, and untrusted-data boundary declarations.

Merge recommendation: ✅ Ready to merge

Blockers (if any — list every issue that MUST be fixed before merge, each prefixed with ❌):

No blockers found.

Non-blocking improvements listed in Recommendations are all polish items that can be addressed in a future patch release.


Generated by Claude AI via Anthropic API — review the full report before approving.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

🔨 Phase 2: Build Verification — ✅ PASSED

Plugin: lido-plugin | Language: rust
Source: @

Compiled from developer source code by our CI. Users install our build artifacts.

Build succeeded. Compiled artifact uploaded as workflow artifact.


Source integrity: commit SHA `` is the content fingerprint.

mig-pre CI Phase 1 [E151] enforces '## Quick Start' as a required
section. okx/main has been renaming this section to '## How it Works'
in newer plugins, but mig-pre's lint hasn't been updated to accept
that. Until the lint diverges or okx reverts, keep mig-pre's standard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@GeoGu360 GeoGu360 added ci-approved Maintainer reviewed PR; allows Phase 1/2/3 CI to run and removed ci-approved Maintainer reviewed PR; allows Phase 1/2/3 CI to run labels May 7, 2026
@GeoGu360 GeoGu360 added the approved-for-publish Triggers Phase 4: compile + publish + merge label May 7, 2026
@plugin-store-bot plugin-store-bot Bot merged commit 43cebab into mig-pre:main May 7, 2026
31 checks passed
@plugin-store-bot
Copy link
Copy Markdown

✅ Phase 4: Publish Complete

Plugins: lido-plugin

  • ✅ Build: 9 architectures compiled
  • ✅ Release: GitHub Release created
  • ✅ Pre-flight: injected into SKILL.md
  • ✅ Registry: registry.json updated
  • ✅ Merged to main

View workflow run


Published by Plugin Store CI

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

Labels

ai-reviewed approved-for-publish Triggers Phase 4: compile + publish + merge ci-approved Maintainer reviewed PR; allows Phase 1/2/3 CI to run plugin-update structure-validated

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant