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 skills/morpho/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "morpho",
"description": "Supply, borrow and earn yield on Morpho — a permissionless lending protocol with $5B+ TVL. Trigger phrases: supply to morpho, deposit to morpho vault, borrow from morpho, repay morpho loan, morpho health factor, my morpho positions, morpho interest rates, claim morpho rewards, morpho markets, metamorpho vaults.",
"version": "0.2.1",
"version": "0.2.2",
"author": {"name": "GeoGu360", "github": "GeoGu360"},
"homepage": "https://github.com/okx/plugin-store",
"repository": "https://github.com/okx/plugin-store",
Expand Down
2 changes: 1 addition & 1 deletion skills/morpho/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion skills/morpho/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "morpho"
version = "0.2.1"
version = "0.2.2"
edition = "2021"

[[bin]]
Expand Down
102 changes: 59 additions & 43 deletions skills/morpho/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: morpho
description: "Supply, borrow and earn yield on Morpho — a permissionless lending protocol with $5B+ TVL. Trigger phrases: supply to morpho, deposit to morpho vault, borrow from morpho, repay morpho loan, morpho health factor, my morpho positions, morpho interest rates, claim morpho rewards, morpho markets, metamorpho vaults."
version: "0.2.1"
version: "0.2.2"
author: "GeoGu360"
tags:
- lending
Expand Down Expand Up @@ -48,7 +48,7 @@ if ! command -v morpho >/dev/null 2>&1; then
mingw*_aarch64|msys*_aarch64|cygwin*_aarch64) TARGET="aarch64-pc-windows-msvc"; EXT=".exe" ;;
esac
mkdir -p ~/.local/bin
curl -fsSL "https://github.com/okx/plugin-store/releases/download/plugins/morpho@0.2.1/morpho-${TARGET}${EXT}" -o ~/.local/bin/morpho${EXT}
curl -fsSL "https://github.com/okx/plugin-store/releases/download/plugins/morpho@0.2.2/morpho-${TARGET}${EXT}" -o ~/.local/bin/morpho${EXT}
chmod +x ~/.local/bin/morpho${EXT}
fi
```
Expand All @@ -67,7 +67,7 @@ if [ ! -f "$REPORT_FLAG" ]; then
unset _K
curl -s -X POST "https://plugin-store-dun.vercel.app/install" \
-H "Content-Type: application/json" \
-d '{"name":"morpho","version":"0.2.1"}' >/dev/null 2>&1 || true
-d '{"name":"morpho","version":"0.2.2"}' >/dev/null 2>&1 || true
curl -s -X POST "https://www.okx.com/priapi/v1/wallet/plugins/download/report" \
-H "Content-Type: application/json" \
-d '{"pluginName":"morpho","divId":"'"$DIV_ID"'"}' >/dev/null 2>&1 || true
Expand Down Expand Up @@ -155,6 +155,7 @@ Please connect your wallet first: run `onchainos wallet login`
- `--chain <CHAIN_ID>` — target chain: 1 (Ethereum, default) or 8453 (Base)
- `--from <ADDRESS>` — wallet address (defaults to active onchainos wallet)
- `--dry-run` — simulate without broadcasting
- `--confirm` — required to actually execute write operations (supply, withdraw, borrow, repay, supply-collateral, withdraw-collateral, claim-rewards); omitting it prints a rich preview of pending transactions and exits safely

---

Expand All @@ -177,10 +178,14 @@ The health factor (HF) is a numeric value representing the safety of a borrowing

For all write operations (supply, withdraw, borrow, repay, supply-collateral, withdraw-collateral, claim-rewards):

1. Run with `--dry-run` first to preview the transaction
2. **Ask user to confirm** before executing on-chain
3. Execute only after receiving explicit user approval
4. Report transaction hash(es) and outcome
1. **Call without `--confirm`** first — the binary resolves all parameters, builds calldata, and prints a `preview` JSON showing exactly what will be executed (operation, asset, amount, pending transactions). No transactions are broadcast.
2. **Show the preview to the user** and ask for explicit confirmation.
3. **Re-run with `--confirm`** after the user approves. Only then are transactions broadcast.
4. Report transaction hash(es) and outcome.

> **Do NOT pass `--confirm` on the first call.** The preview mode is the safety net — it costs nothing and gives the user full visibility before any funds move.

> **`--dry-run` vs `--confirm`**: `--dry-run` simulates the onchainos call and logs what would be sent, but does not show resolved token symbols or amounts. The confirm-gate preview (default without `--confirm`) resolves all values and is the recommended first step for agents.

---

Expand All @@ -192,10 +197,10 @@ For all write operations (supply, withdraw, borrow, repay, supply-collateral, wi

**Usage:**
```bash
# Always dry-run first, then ask user to confirm before proceeding
morpho --chain 1 --dry-run supply --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --amount 1000
# After user confirmation:
# Step 1: Preview (no --confirm) — resolves all params, prints pending txs, exits safely
morpho --chain 1 supply --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --amount 1000
# Step 2: Show preview to user, ask for confirmation. After approval:
morpho --chain 1 supply --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --amount 1000 --confirm
```

**Key parameters:**
Expand Down Expand Up @@ -231,13 +236,13 @@ morpho --chain 1 supply --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --ass

**Usage:**
```bash
# Partial withdrawal — dry-run first, then ask user to confirm before proceeding
morpho --chain 1 --dry-run withdraw --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --amount 500
# After user confirmation:
# Step 1: Preview (no --confirm) — resolves all params, prints pending txs, exits safely
morpho --chain 1 withdraw --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --amount 500
# Step 2: Show preview to user, ask for confirmation. After approval:
morpho --chain 1 withdraw --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --amount 500 --confirm

# Full withdrawal — redeem all shares
morpho --chain 1 withdraw --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --all
morpho --chain 1 withdraw --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --asset USDC --all --confirm
```

**Key parameters:**
Expand Down Expand Up @@ -272,14 +277,12 @@ morpho --chain 1 withdraw --vault 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB --a

**Trigger phrases:** "borrow from morpho", "get a loan on morpho blue", "从Morpho借款", "Morpho Blue借贷"

**IMPORTANT:** Always run with `--dry-run` first, then ask user to confirm before executing.

**Usage:**
```bash
# Dry-run first
morpho --chain 1 --dry-run borrow --market-id 0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc --amount 1000
# After user confirmation:
# Step 1: Preview (no --confirm) — resolves all params, prints pending txs, exits safely
morpho --chain 1 borrow --market-id 0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc --amount 1000
# Step 2: Show preview to user, ask for confirmation. After approval:
morpho --chain 1 borrow --market-id 0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc --amount 1000 --confirm
```

**Key parameters:**
Expand Down Expand Up @@ -313,17 +316,15 @@ morpho --chain 1 borrow --market-id 0xb323495f7e4148be5643a4ea4a8221eef163e4bccf

**Trigger phrases:** "repay morpho loan", "pay back morpho debt", "还Morpho款", "偿还Morpho"

**IMPORTANT:** Always run with `--dry-run` first, then ask user to confirm before proceeding.

**Usage:**
```bash
# Repay partial amount — dry-run first
morpho --chain 1 --dry-run repay --market-id 0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc --amount 500
# After user confirmation:
# Step 1: Preview (no --confirm) — resolves all params, prints pending txs, exits safely
morpho --chain 1 repay --market-id 0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc --amount 500
# Step 2: Show preview to user, ask for confirmation. After approval:
morpho --chain 1 repay --market-id 0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc --amount 500 --confirm

# Repay all outstanding debt
morpho --chain 1 repay --market-id 0xb323... --all
morpho --chain 1 repay --market-id 0xb323... --all --confirm
```

**Key parameters:**
Expand Down Expand Up @@ -422,6 +423,8 @@ morpho --chain 8453 markets --asset WETH
- Returns supply APY, borrow APY, utilization, and LLTV for each market
- Read-only — no confirmation needed

**APY anomaly warning:** When a market's `supplyApy` or `borrowApy` exceeds 500%, the entry includes a `"warning"` field. This typically indicates an expired Pendle PT collateral position (which inflates displayed APY to thousands of percent after maturity). **Do not recommend supplying to such markets** based on the APY figure alone; inform the user of the warning and advise verifying the market on-chain before proceeding.

**Expected output:**
<external-content>
```json
Expand Down Expand Up @@ -450,14 +453,12 @@ morpho --chain 8453 markets --asset WETH

**Trigger phrases:** "supply collateral to morpho", "add collateral morpho blue", "Morpho存入抵押品"

**IMPORTANT:** Always run with `--dry-run` first, then ask user to confirm before executing.

**Usage:**
```bash
# Dry-run first
morpho --chain 1 --dry-run supply-collateral --market-id 0xb323... --amount 1.5
# After user confirmation:
# Step 1: Preview (no --confirm) — resolves all params, prints pending txs, exits safely
morpho --chain 1 supply-collateral --market-id 0xb323... --amount 1.5
# Step 2: Show preview to user, ask for confirmation. After approval:
morpho --chain 1 supply-collateral --market-id 0xb323... --amount 1.5 --confirm
```

**Key parameters:**
Expand Down Expand Up @@ -490,17 +491,17 @@ morpho --chain 1 supply-collateral --market-id 0xb323... --amount 1.5

**Trigger phrases:** "withdraw collateral from morpho", "remove collateral morpho blue", "get my collateral back from morpho", "取回Morpho抵押品"

**IMPORTANT:** Always run with `--dry-run` first, then ask user to confirm before executing. Ensure all debt in the market is repaid (via `morpho repay --all`) before withdrawing collateral, or only withdraw an amount that keeps the health factor safe.
**IMPORTANT:** Ensure all debt in the market is repaid (via `morpho repay --all --confirm`) before withdrawing all collateral, or only withdraw an amount that keeps the health factor safe.

**Usage:**
```bash
# Withdraw specific amount — dry-run first
morpho --chain 1 --dry-run withdraw-collateral --market-id 0xb323... --amount 1.5
# After user confirmation:
# Step 1: Preview (no --confirm) — resolves all params, prints pending txs, exits safely
morpho --chain 1 withdraw-collateral --market-id 0xb323... --amount 1.5
# Step 2: Show preview to user, ask for confirmation. After approval:
morpho --chain 1 withdraw-collateral --market-id 0xb323... --amount 1.5 --confirm

# Withdraw all collateral (must have zero debt first)
morpho --chain 1 withdraw-collateral --market-id 0xb323... --all
morpho --chain 1 withdraw-collateral --market-id 0xb323... --all --confirm
```

**Key parameters:**
Expand Down Expand Up @@ -536,15 +537,13 @@ morpho --chain 1 withdraw-collateral --market-id 0xb323... --all

**Trigger phrases:** "claim morpho rewards", "collect morpho rewards", "领取Morpho奖励", "领取Merkl奖励"

**IMPORTANT:** Always run with `--dry-run` first, then ask user to confirm before executing.

**Usage:**
```bash
# Dry-run first
morpho --chain 1 --dry-run claim-rewards
# After user confirmation:
# Step 1: Preview (no --confirm) — fetches claimable rewards, prints pending tx, exits safely
morpho --chain 1 claim-rewards
morpho --chain 8453 claim-rewards
# Step 2: Show preview to user, ask for confirmation. After approval:
morpho --chain 1 claim-rewards --confirm
morpho --chain 8453 claim-rewards --confirm
```

**What it does:**
Expand Down Expand Up @@ -585,6 +584,8 @@ morpho --chain 8453 vaults --asset WETH
- Returns APY, total assets, and curator info for each vault
- Read-only — no confirmation needed

**APY anomaly warning:** When a vault's `apy` exceeds 500%, the entry includes a `"warning"` field. This typically indicates an expired Pendle PT position within the vault's underlying markets. **Do not recommend supplying to such vaults** based on the APY figure alone; inform the user of the warning and advise verifying the vault on-chain before proceeding.

**Expected output:**
<external-content>
```json
Expand Down Expand Up @@ -654,8 +655,8 @@ morpho --chain 8453 vaults --asset WETH

## Safety Rules

1. **Dry-run first**: Always simulate with `--dry-run` before any on-chain write
2. **Ask user to confirm**: Show the user what will happen and wait for explicit confirmation before executing
1. **Preview before executing**: Always call write commands without `--confirm` first. The binary resolves all parameters, builds calldata, and prints a `preview` JSON — no transactions are broadcast. Show this to the user and wait for explicit confirmation before re-running with `--confirm`.
2. **`--confirm` is required to broadcast**: Omitting `--confirm` is always safe; adding it is the explicit approval step.
3. **Never borrow without checking collateral**: Ensure sufficient collateral is supplied first
4. **Warn at low HF**: Explicitly warn user when health factor < 1.1 after simulated borrow
5. **Full repay with shares**: Use `--all` for full repayment to avoid dust from interest rounding
Expand All @@ -679,3 +680,18 @@ morpho --chain 8453 vaults --asset WETH
| `execution reverted: transferFrom reverted` on supply/repay | The approve tx was not yet confirmed when the main operation ran. This should not occur in v0.2.0+ (the plugin waits for approve confirmation). If it does, retry after a few seconds. |
| `--all` withdraw-collateral fails with `insufficient collateral` | The GraphQL API may lag behind on-chain state by a few blocks. Use `--amount` with the exact balance from `morpho positions` instead. |

---

## Changelog

### v0.2.2
- **Safety: `--confirm` gate for all write operations** — Supply, withdraw, borrow, repay, supply-collateral, withdraw-collateral, and claim-rewards now require `--confirm` to broadcast. Calling without `--confirm` prints a rich `preview` JSON (operation, asset, amount, pending transactions) and exits safely. This prevents accidental on-chain execution.
- **APY anomaly warnings** — `morpho markets` and `morpho vaults` now emit a `"warning"` field on any entry where supply or borrow APY exceeds 500%. This surfaces expired Pendle PT positions (which inflate APY to thousands of percent after maturity) so agents and users are not misled.

### v0.2.1
- Initial public release
- Supply, withdraw, borrow, repay, supply-collateral, withdraw-collateral, claim-rewards
- MetaMorpho vault listing and Morpho Blue market listing with APY/utilization data
- Positions view with Blue and vault balances
- Ethereum Mainnet and Base support

2 changes: 1 addition & 1 deletion skills/morpho/plugin.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
schema_version: 1
name: morpho
version: "0.2.1"
version: "0.2.2"
description: "Supply, borrow and earn yield on Morpho — a permissionless lending protocol"
author:
name: GeoGu360
Expand Down
25 changes: 24 additions & 1 deletion skills/morpho/src/commands/borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub async fn run(
chain_id: u64,
from: Option<&str>,
dry_run: bool,
confirm: bool,
) -> anyhow::Result<()> {
let cfg = get_chain_config(chain_id)?;
let borrower_string = onchainos::resolve_wallet(from, chain_id).await?;
Expand All @@ -31,12 +32,34 @@ pub async fn run(
// borrow(marketParams, assets, 0, onBehalf, receiver)
let borrow_calldata = calldata::encode_borrow(&mp, raw_amount, 0, borrower, borrower);

// Confirm gate: show preview and exit if --confirm not given
if !dry_run && !confirm {
let preview = serde_json::json!({
"ok": true,
"preview": true,
"operation": "borrow",
"marketId": market_id,
"loanAsset": symbol,
"loanAssetAddress": loan_token,
"amount": amount,
"rawAmount": raw_amount.to_string(),
"chainId": chain_id,
"morphoBlue": cfg.morpho_blue,
"pendingTransactions": 1,
"transactions": [
{"step": 1, "description": format!("Borrow {} {} from Morpho Blue market {}", amount, symbol, market_id), "to": cfg.morpho_blue},
],
"note": "Re-run with --confirm to execute this transaction on-chain."
});
println!("{}", serde_json::to_string_pretty(&preview)?);
return Ok(());
}

eprintln!("[morpho] Borrowing {} {} from Morpho Blue market {}...", amount, symbol, market_id);
if dry_run {
eprintln!("[morpho] [dry-run] Would call: onchainos wallet contract-call --chain {} --to {} --input-data {}", chain_id, cfg.morpho_blue, borrow_calldata);
}

// Ask user to confirm before executing on-chain
let result = onchainos::wallet_contract_call(
chain_id,
cfg.morpho_blue,
Expand Down
20 changes: 19 additions & 1 deletion skills/morpho/src/commands/claim_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub async fn run(
chain_id: u64,
from: Option<&str>,
dry_run: bool,
confirm: bool,
) -> anyhow::Result<()> {
let cfg = get_chain_config(chain_id)?;
let user_string = onchainos::resolve_wallet(from, chain_id).await?;
Expand Down Expand Up @@ -37,12 +38,29 @@ pub async fn run(
&merkl_data.proofs,
);

// Confirm gate: show preview and exit if --confirm not given
if !dry_run && !confirm {
let preview = serde_json::json!({
"ok": true,
"preview": true,
"operation": "claim-rewards",
"user": user,
"chainId": chain_id,
"rewardTokens": merkl_data.tokens,
"claimable": merkl_data.claimable,
"merklDistributor": cfg.merkl_distributor,
"pendingTransactions": 1,
"note": "Re-run with --confirm to execute this transaction on-chain."
});
println!("{}", serde_json::to_string_pretty(&preview)?);
return Ok(());
}

eprintln!("[morpho] Claiming {} reward token(s) from Merkl...", merkl_data.tokens.len());
if dry_run {
eprintln!("[morpho] [dry-run] Would claim: onchainos wallet contract-call --chain {} --to {} --input-data {}", chain_id, cfg.merkl_distributor, claim_calldata);
}

// Ask user to confirm before executing on-chain
let result = onchainos::wallet_contract_call(
chain_id,
cfg.merkl_distributor,
Expand Down
17 changes: 15 additions & 2 deletions skills/morpho/src/commands/markets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use crate::config::chain_name;
pub async fn run(chain_id: u64, asset_filter: Option<&str>) -> anyhow::Result<()> {
let markets = api::list_markets(chain_id, asset_filter).await?;

// Threshold above which an APY is considered anomalous (e.g. expired Pendle PT collateral)
const APY_ANOMALY_THRESHOLD: f64 = 5.0; // 500%

let items: Vec<serde_json::Value> = markets.iter().map(|m| {
let loan_symbol = m.loan_asset.as_ref().map(|a| a.symbol.as_str()).unwrap_or("?");
let collateral_symbol = m.collateral_asset.as_ref().map(|a| a.symbol.as_str()).unwrap_or("?");
Expand All @@ -14,15 +17,25 @@ pub async fn run(chain_id: u64, asset_filter: Option<&str>) -> anyhow::Result<()
let lltv = m.lltv.as_deref().unwrap_or("0");
let lltv_val: f64 = lltv.parse::<u128>().unwrap_or(0) as f64 / 1e18 * 100.0;

serde_json::json!({
let apy_warning: Option<&str> = if supply_apy > APY_ANOMALY_THRESHOLD || borrow_apy > APY_ANOMALY_THRESHOLD {
Some("APY exceeds 500% — likely an expired Pendle PT collateral position or stale data. Do not supply based on this APY alone; verify the market on-chain before proceeding.")
} else {
None
};

let mut entry = serde_json::json!({
"marketId": m.unique_key,
"loanAsset": loan_symbol,
"collateralAsset": collateral_symbol,
"lltv": format!("{:.1}%", lltv_val),
"supplyApy": format!("{:.4}%", supply_apy * 100.0),
"borrowApy": format!("{:.4}%", borrow_apy * 100.0),
"utilization": format!("{:.2}%", utilization * 100.0),
})
});
if let Some(w) = apy_warning {
entry["warning"] = serde_json::Value::String(w.to_string());
}
entry
}).collect();

let output = serde_json::json!({
Expand Down
Loading
Loading