diff --git a/skills/xlayer-bridge-yield/.claude-plugin/plugin.json b/skills/xlayer-bridge-yield/.claude-plugin/plugin.json new file mode 100644 index 000000000..1265503fa --- /dev/null +++ b/skills/xlayer-bridge-yield/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "xlayer-bridge-yield", + "description": "Bridge assets to X Layer and auto-optimize stablecoin yield farming with risk management", + "version": "1.0.0", + "author": { + "name": "oliva9595" + }, + "license": "MIT", + "keywords": ["xlayer", "bridge", "yield", "stablecoin", "defi"] +} diff --git a/skills/xlayer-bridge-yield/LICENSE b/skills/xlayer-bridge-yield/LICENSE new file mode 100644 index 000000000..122c99fcd --- /dev/null +++ b/skills/xlayer-bridge-yield/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 oliva9595 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/skills/xlayer-bridge-yield/README.md b/skills/xlayer-bridge-yield/README.md new file mode 100644 index 000000000..c50dbbb92 --- /dev/null +++ b/skills/xlayer-bridge-yield/README.md @@ -0,0 +1,52 @@ +# xlayer-bridge-yield + +Bridge assets to X Layer (OKX's L2) and auto-optimize stablecoin yield farming with built-in risk management. + +## What It Does + +This OKX OnchainOS plugin provides a seamless "bridge and farm" experience: + +1. **Bridge Optimizer** — Compares cross-chain bridge routes to find the cheapest and fastest path to X Layer +2. **Yield Scanner** — Scans DeFi protocols on X Layer and ranks yield pools by APY, TVL, and security risk +3. **Full Pipeline** — One command to bridge + deposit into the best pool, with pre-execution safety checks +4. **Position Monitor** — Tracks active positions with depeg alerts, stop-loss triggers, and rebalance suggestions + +## Key Features + +- **Dry-run by default** — No real transactions unless you explicitly confirm +- **Security-first** — Mandatory token scans before any deposit +- **Risk management** — Configurable stop-loss, max amounts, and depeg alerts +- **X Layer optimized** — Takes advantage of near-zero gas for frequent rebalancing + +## Install + +```bash +npx skills add okx/plugin-store --skill xlayer-bridge-yield +``` + +## Usage Examples + +``` +"Find the cheapest bridge for 100 USDC from Ethereum to X Layer" +"Show best yield pools on X Layer for USDT" +"Bridge 200 USDC to X Layer and farm the best yield" +"Check my X Layer farming positions" +``` + +## Requirements + +- onchainos CLI installed (`npx skills add okx/onchainos-skills`) +- Python 3.8+ +- Funded wallet on source chain + +## Safety + +- Default dry-run mode +- Pre-trade security scanning +- Configurable stop-loss (-5% default) +- Max transaction amount ($1,000 default) +- Stablecoin depeg alerts (0.5% threshold) + +## License + +MIT diff --git a/skills/xlayer-bridge-yield/SKILL.md b/skills/xlayer-bridge-yield/SKILL.md new file mode 100644 index 000000000..aef9262f9 --- /dev/null +++ b/skills/xlayer-bridge-yield/SKILL.md @@ -0,0 +1,295 @@ +--- +name: xlayer-bridge-yield +description: "Bridge assets to X Layer and auto-optimize stablecoin yield farming with risk management" +version: "1.0.0" +author: "oliva9595" +tags: + - xlayer + - bridge + - yield + - stablecoin + - defi +--- + +# X Layer Bridge + Yield Optimizer + +## Overview + +This skill enables the AI agent to bridge stablecoins (USDC, USDT, DAI) from any supported chain to X Layer (OKX's L2, chain ID `196`) and automatically find, enter, and monitor the best yield farming positions. It combines cross-chain bridge route optimization with on-chain yield scanning to provide a seamless "bridge and farm" experience. + +**Key capabilities:** +- Compare bridge routes across multiple paths for cost and speed +- Scan and rank X Layer DeFi yield pools by APY, TVL, and risk +- Execute the full bridge-to-farm pipeline with safety checks +- Monitor active positions with depeg alerts and rebalance suggestions + +## Trigger Keywords + +### Activate this skill when the user mentions: +- "bridge to X Layer", "bridge USDC", "bridge USDT", "cross-chain to X Layer", "move to X Layer", "transfer to X Layer", "send to X Layer" +- "yield on X Layer", "farm on X Layer", "best APY X Layer", "stablecoin yield", "yield farming", "find yield pools" +- "bridge and farm", "bridge and deposit", "bridge and earn", "move and farm" +- "check positions", "my yield", "farming status", "how is my farming", "position monitor", "X Layer positions" +- "cheapest bridge", "fastest bridge", "compare bridge routes", "bridge cost", "bridge fee" +- "depeg alert", "stop loss", "rebalance" + +### DO NOT activate this skill when: +- User asks about swapping tokens on the SAME chain without bridging → use `okx-dex-swap` +- User asks about wallet balance only without yield context → use `okx-wallet-portfolio` +- User asks about token security only → use `okx-security` +- User mentions a destination chain OTHER than X Layer (chain 196) +- User discusses NFT trading, meme coins, or prediction markets +- User asks about lending/borrowing on Ethereum L1 protocols (Aave, Compound) + +## Pre-flight Checks + +Before using this skill, ensure: + +1. The `onchainos` CLI is installed and configured: + ```bash + npx skills add okx/onchainos-skills + ``` +2. Python 3.8+ is available (for helper scripts): + ```bash + python3 --version + ``` +3. The user has a funded wallet on the source chain (Ethereum, BSC, Polygon, Arbitrum, or Solana) +4. The `onchainos wallet` is logged in and authenticated + +## Safety Configuration + +This plugin operates in **dry-run mode by default**. All commands simulate transactions without executing unless the user explicitly confirms. + +### Default Safety Parameters + +| Parameter | Default | Description | +|-----------|---------|-------------| +| `mode` | `dry-run` | No real transactions unless user says "execute" or "confirm" | +| `max_amount` | `1000` | Maximum USD value per single transaction | +| `stop_loss` | `-5%` | Alert + suggest exit if position drops below this | +| `min_pool_tvl` | `50000` | Skip pools with TVL below $50,000 | +| `min_pool_age_days` | `30` | Skip pools younger than 30 days | +| `depeg_alert_threshold` | `0.005` | Alert if stablecoin deviates > 0.5% from peg | + +The user can override any parameter by specifying it in their request (e.g., "set max amount to 5000"). + +--- + +## Commands + +### 1. Bridge Route Scan + +Compares cross-chain bridge routes to find the cheapest and fastest path to X Layer. + +```bash +onchainos swap quote --from --to --amount --chain --dest-chain 196 --format json +``` + +**When to use**: When the user asks "find cheapest bridge to X Layer", "compare bridge routes", "how much does it cost to bridge USDC", "bridge fees to X Layer", "fastest way to move USDT to X Layer", "compare cross-chain options", or "which bridge should I use". + +**Workflow**: +1. Identify the source chain and token from user's request +2. Check user's balance on source chain (to verify they have enough tokens before quoting): + ```bash + onchainos portfolio all-balances --address --chain --format json + ``` +3. Get cross-chain quotes for the amount: + ```bash + onchainos swap quote --from --to --amount --chain --dest-chain 196 --format json + ``` +4. Run the bridge optimizer script to rank routes: + ```bash + python3 scripts/bridge_optimizer.py --quotes '' + ``` +5. Present results as a table: + +| Route | Fee | Est. Time | Slippage | Score | +|-------|-----|-----------|----------|-------| +| Route A | $0.50 | ~2 min | 0.01% | 9.5 | +| Route B | $1.20 | ~1 min | 0.02% | 8.8 | + +**Output format**: Present results as a **markdown table** with columns: Route, Fee (USD), Est. Time, Slippage (%), Score. Always end with a one-sentence recommendation: "Recommended: [Route] — cheapest at $X with ~Y min arrival." + +**If no routes found**: Tell the user that the token/amount may not be supported for cross-chain to X Layer. Suggest trying USDC or USDT, or a different source chain. + +--- + +### 2. Yield Pool Scan + +Scans DeFi protocols on X Layer to find the best yield farming opportunities for stablecoins. + +**When to use**: When the user asks "show yield pools on X Layer", "best APY on X Layer", "where to farm USDC on X Layer", "stablecoin farming options", "highest yield pools", "safe yield X Layer", or "compare pools". + +**Workflow**: +1. Search for stablecoin-related tokens and pools on X Layer: + ```bash + onchainos token search --query USDC --chain 196 --format json + onchainos token search --query USDT --chain 196 --format json + ``` +2. Get market data for discovered pools: + ```bash + onchainos market price --address --chain 196 --format json + ``` +3. Run security scan on each pool token (required because X Layer is a newer L2 and pool contracts may not be audited): + ```bash + onchainos security token-scan --address --chain 196 --format json + ``` +4. Run the yield scanner script to rank pools: + ```bash + python3 scripts/yield_scanner.py --tokens '' --market '' --security '' + ``` +5. Present results: + +| Pool | Protocol | APY | TVL | Risk | Score | +|------|----------|-----|-----|------|-------| +| USDC/USDT | DEX-A | 8.5% | $2.1M | LOW | 9.2 | +| USDC/OKB | DEX-B | 12.3% | $850K | MEDIUM | 7.8 | + +**Output format**: Present results as a **markdown table** with columns: Pool, Protocol, APY (%), TVL ($), Risk (LOW/MEDIUM/HIGH), Score. Always end with: "Top pick: [Pool] — [APY]% APY with [Risk] risk." + +**Filtering rules**: +- Skip pools with TVL < `min_pool_tvl` (default $50,000) +- Skip pools where security scan returns CRITICAL risk +- Flag pools younger than `min_pool_age_days` as HIGH risk + +--- + +### 3. Bridge and Farm (Full Pipeline) + +Executes the complete workflow: bridge assets from source chain to X Layer, then deposit into the best yield pool. + +**When to use**: When the user asks "bridge and farm", "bridge 100 USDC to X Layer and find yield", "move my USDT to X Layer for farming", "bridge and earn", "one-click yield", "auto farm on X Layer", or any request combining bridging with yield/farming. + +**Workflow**: +1. **Validate amount**: Ensure amount <= `max_amount`. If exceeded, warn user and ask for confirmation. +2. **Run Bridge Route Scan** (Command 1) to find the best route. +3. **Run Yield Pool Scan** (Command 2) to find the best pool. +4. **Security pre-check**: + ```bash + onchainos security token-scan --address --chain 196 --format json + ``` + If risk level is CRITICAL → **STOP and warn user. Do not proceed.** +5. **Present plan to user** — show: + - Bridge route (fee, time, slippage) + - Target yield pool (APY, TVL, risk) + - Total estimated cost + - Expected daily/monthly yield +6. **WAIT FOR USER CONFIRMATION** — Do not proceed without explicit "confirm", "execute", or "yes". +7. **Execute bridge**: + ```bash + onchainos swap swap --from --to --amount --chain --dest-chain 196 --format json + ``` +8. **Wait for bridge completion** — poll status every 30 seconds: + ```bash + onchainos portfolio all-balances --chain 196 --format json + ``` +9. **Execute yield deposit** (if the pool requires a swap first): + ```bash + onchainos swap swap --from --to --amount --chain 196 --format json + ``` +10. **Report result**: Transaction hash, amount deposited, expected APY, position ID. + +**If bridge fails**: Show error, suggest retry. Do not auto-retry. +**If in dry-run mode**: Show the full plan with simulated values but do NOT execute steps 7-10. Print `[DRY-RUN] No transactions were executed.` + +--- + +### 4. Position Monitor + +Checks the current status of yield farming positions on X Layer. + +**When to use**: When the user asks "check my positions", "how is my X Layer farming doing", "show my yield status", "am I making money", "any alerts", "depeg check", "position P/L", or "rebalance suggestions". + +**Workflow**: +1. Get current balances on X Layer: + ```bash + onchainos portfolio all-balances --chain 196 --format json + ``` +2. Get current market prices for held tokens: + ```bash + onchainos market price --address --chain 196 --format json + ``` +3. Run position monitor script: + ```bash + python3 scripts/position_monitor.py --balances '' --prices '' + ``` +4. Present position summary: + +| Token | Amount | Value | Change | Status | +|-------|--------|-------|--------|--------| +| USDC | 100.5 | $100.50 | +$0.50 | HEALTHY | +| LP-USDC-USDT | 95.2 | $95.18 | -$4.82 | ⚠️ CHECK | + +5. **Alert conditions** (auto-triggered): + - Depeg alert: stablecoin price deviates > 0.5% from $1.00 + - Loss alert: position value dropped > `stop_loss` threshold + - APY change: significant APY decrease detected + +**Output format**: Present as a **markdown table** with columns: Token, Amount, Value ($), Change (%), Status. Follow with any alerts as bold warnings. End with: "Overall: $X total value, [healthy/action needed]." If alerts exist, add: "⚠️ Suggested action: [specific recommendation]." + +--- + +## Examples + +### Example 1: Quick Bridge Scan + +**User**: "Find the cheapest way to bridge 500 USDC from Ethereum to X Layer" + +1. Check balance: `onchainos portfolio all-balances --chain 1 --format json` +2. Get quotes: `onchainos swap quote --from 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --to 0x... --amount 500000000 --chain 1 --dest-chain 196 --format json` +3. Run optimizer: `python3 scripts/bridge_optimizer.py --quotes ''` +4. Present: "The cheapest route costs $0.45 via [Bridge], arriving in ~3 minutes." + +### Example 2: Full Bridge and Farm + +**User**: "Bridge 200 USDT to X Layer and farm the best yield" + +1. Scan bridge routes → best route costs $0.30 +2. Scan yield pools → best pool is USDT/USDC at 7.2% APY +3. Present plan: "Bridge 200 USDT ($0.30 fee) → deposit in USDT/USDC pool (7.2% APY, ~$0.04/day yield). Confirm?" +4. User says "yes" → execute bridge → wait → deposit +5. Report: "Done! 199.70 USDT deposited. Expected yield: $1.18/month." + +### Example 3: Position Check with Alert + +**User**: "Check my X Layer farming positions" + +1. Get balances on chain 196 +2. Calculate P/L +3. "Your USDC/USDT position: $198.50 (entry $200.00, -0.75%). APY currently 6.8%. Status: HEALTHY. No alerts." + +--- + +## Error Handling + +| Error | Cause | Resolution | +|-------|-------|------------| +| "Insufficient balance on source chain" | Not enough tokens to bridge | Ask user to check their balance or reduce the amount | +| "No bridge routes found" | Token/amount not supported for cross-chain | Suggest trying USDC or USDT, or a different source chain | +| "Bridge transaction failed" | Network congestion or insufficient gas | Ask user to retry. Do not auto-retry | +| "Pool token security: CRITICAL" | Target pool has critical security issues | STOP immediately. Warn user. Suggest alternative pools | +| "Amount exceeds max_amount" | User requested more than safety limit | Show current limit, ask user to confirm override or reduce amount | +| "onchainos not found" | CLI not installed | Run `npx skills add okx/onchainos-skills` | +| "Wallet not authenticated" | Not logged in | Run `onchainos wallet login` | +| "Python not found" | Python 3 not installed | Ask user to install Python 3.8+ | + +--- + +## Security Notices + +> **RISK DISCLAIMER**: This plugin interacts with DeFi protocols and performs on-chain transactions. DeFi carries inherent risks including but not limited to: smart contract vulnerabilities, impermanent loss, stablecoin depeg events, bridge exploits, and loss of funds. Past APY does not guarantee future returns. Always review transactions before confirming. This plugin is provided as-is with no warranty. Use at your own risk. + +- **Risk Level**: Advanced — this plugin can execute transactions autonomously when not in dry-run mode +- **Default Mode**: Dry-run (no real transactions unless user explicitly confirms) +- **Private Keys**: This plugin NEVER handles, stores, or requests private keys. All signing is done through onchainos secure wallet (TEE) +- **Safety Checks**: Pre-execution security scans are mandatory and cannot be bypassed +- **Maximum Limits**: Configurable per-transaction caps prevent accidental large trades +- **Stop-Loss**: Automated alerts when positions exceed loss thresholds + +## Skill Routing + +- For token swaps only → use `okx-dex-swap` skill +- For wallet balances → use `okx-wallet-portfolio` skill +- For security scanning → use `okx-security` skill +- For pre-trade risk assessment → use `agent-risk-firewall` skill +- For smart money signals on X Layer → use `xlayer-alpha-hunter` skill (if installed) diff --git a/skills/xlayer-bridge-yield/SUMMARY.md b/skills/xlayer-bridge-yield/SUMMARY.md new file mode 100644 index 000000000..3750196b0 --- /dev/null +++ b/skills/xlayer-bridge-yield/SUMMARY.md @@ -0,0 +1,32 @@ +## Overview + +xlayer-bridge-yield is a DeFi automation plugin that bridges stablecoins to X Layer (OKX's L2) and optimizes yield farming positions with built-in risk management. + +Core operations: + +- Compare cross-chain bridge routes for cost and speed optimization +- Scan and rank X Layer yield pools by APY, TVL, and risk score +- Execute the full bridge-to-farm pipeline with pre-execution safety checks +- Monitor active positions with depeg alerts and rebalance suggestions + +Tags: `xlayer` `bridge` `yield` `stablecoin` `defi` `defi-protocol` + +## Prerequisites + +- No IP restrictions +- Supported source chains: Ethereum (1), BSC (56), Polygon (137), Arbitrum (42161), Solana +- Supported destination: X Layer (chain ID 196) +- Supported tokens: USDC, USDT, DAI +- onchainos CLI installed and authenticated (`npx skills add okx/onchainos-skills`) +- Python 3.8+ installed (for helper scripts) +- A funded wallet on the source chain with sufficient tokens and gas + +## Quick Start + +1. **Scan bridge routes**: Ask the agent "find the cheapest bridge for 100 USDC from Ethereum to X Layer". The agent compares available cross-chain routes and presents a ranked table with fees, estimated time, and slippage for each option. + +2. **Scan yield pools**: Ask "show the best yield pools on X Layer for USDT". The agent scans DeFi protocols on X Layer, runs security checks on each pool token, and ranks results by APY adjusted for risk. Pools with critical security issues or low TVL are automatically filtered out. + +3. **Bridge and farm**: Ask "bridge 200 USDC to X Layer and farm the best yield". The agent combines both scans, presents a complete plan (bridge cost + target pool + expected yield), and waits for your explicit confirmation before executing any transaction. Default mode is dry-run — no real transactions occur unless you say "confirm" or "execute". + +4. **Monitor positions**: Ask "check my X Layer farming positions". The agent shows current value, profit/loss, and any alerts (depeg risk, APY changes, stop-loss triggers). If a better pool is available, it suggests rebalancing. diff --git a/skills/xlayer-bridge-yield/plugin.yaml b/skills/xlayer-bridge-yield/plugin.yaml new file mode 100644 index 000000000..062693250 --- /dev/null +++ b/skills/xlayer-bridge-yield/plugin.yaml @@ -0,0 +1,21 @@ +schema_version: 1 +name: xlayer-bridge-yield +version: "1.0.0" +description: "Bridge assets to X Layer and auto-optimize stablecoin yield farming with risk management" +author: + name: "oliva9595" + github: "oliva9595" +license: MIT +category: defi-protocol +tags: + - xlayer + - bridge + - yield + - stablecoin + - defi + +components: + skill: + dir: "." + +api_calls: [] diff --git a/skills/xlayer-bridge-yield/references/xlayer-defi-map.md b/skills/xlayer-bridge-yield/references/xlayer-defi-map.md new file mode 100644 index 000000000..e4e117cf5 --- /dev/null +++ b/skills/xlayer-bridge-yield/references/xlayer-defi-map.md @@ -0,0 +1,70 @@ +# X Layer DeFi Protocol Map + +Reference document for known DeFi protocols on X Layer (chain ID 196). + +## Chain Info + +| Property | Value | +|----------|-------| +| Chain ID | 196 | +| Native Token | OKB | +| Consensus | zkEVM (Polygon CDK) | +| Gas Cost | Near-zero (~$0.001 per tx) | +| Finality | ~10 seconds | +| Bridge | OKX Bridge (native), third-party bridges | + +## Known DEX Protocols + +### Uniswap V3 (X Layer deployment) +- Type: AMM / Concentrated Liquidity +- Pairs: OKB/USDC, OKB/USDT, USDC/USDT, OKB/WETH +- Router: Check onchainos for current addresses + +### Native DEXes +- Several native DEXes may be deployed on X Layer +- Use `onchainos token search --chain 196` to discover current pools + +## Stablecoin Addresses on X Layer + +Use `onchainos token search --query --chain 196 --format json` to get current contract addresses. + +Common stablecoins: +- USDC (bridged) +- USDT (bridged) +- DAI (bridged) + +## Bridge Options + +### OKX Native Bridge +- Source chains: Ethereum, BSC, Polygon, Arbitrum, Avalanche +- Tokens: USDC, USDT, OKB, WETH +- Speed: ~2-5 minutes +- Fee: Very low (subsidized by OKX) + +### Third-Party Bridges +- Available via onchainos cross-chain swap aggregator +- Aggregates multiple bridge protocols for best rates + +## Yield Opportunities + +### LP Farming +- Provide liquidity to DEX pairs +- Earn trading fees + potential liquidity mining rewards + +### Lending (if available) +- Deposit stablecoins to earn interest +- Check `onchainos token search --chain 196` for lending protocol tokens + +## Risk Factors + +1. **Smart Contract Risk**: New protocols on X Layer may not be audited +2. **Bridge Risk**: Cross-chain bridges are historically attack vectors +3. **Liquidity Risk**: Newer pools may have thin liquidity +4. **Depeg Risk**: Bridged stablecoins depend on bridge security +5. **Impermanent Loss**: LP positions in volatile pairs + +## Notes + +- X Layer gas is near-zero, making frequent rebalancing economically viable +- OKB is the native gas token — user needs a small amount for gas +- All addresses should be verified via onchainos before use diff --git a/skills/xlayer-bridge-yield/scripts/bridge_optimizer.py b/skills/xlayer-bridge-yield/scripts/bridge_optimizer.py new file mode 100644 index 000000000..955233a56 --- /dev/null +++ b/skills/xlayer-bridge-yield/scripts/bridge_optimizer.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Bridge route optimizer for xlayer-bridge-yield plugin. + +Reads JSON quote data from onchainos swap quote (cross-chain mode) +and ranks routes by composite score = w_fee * fee_score + w_time * time_score + w_slip * slip_score. + +Usage: + python3 bridge_optimizer.py --quotes '' + python3 bridge_optimizer.py --file quotes.json + +Output: JSON array of ranked routes with scores. +""" + +import argparse +import json +import sys + +# Scoring weights (sum = 1.0) +W_FEE = 0.50 +W_TIME = 0.30 +W_SLIPPAGE = 0.20 + + +def normalize(values, lower_is_better=True): + """Min-max normalize a list of numbers to [0, 10].""" + if not values: + return [] + mn, mx = min(values), max(values) + if mn == mx: + return [10.0] * len(values) + if lower_is_better: + return [10.0 * (mx - v) / (mx - mn) for v in values] + return [10.0 * (v - mn) / (mx - mn) for v in values] + + +def extract_route_fields(route): + """Extract fee, time, slippage from a route object. + + Handles multiple possible JSON shapes from onchainos swap quote. + """ + fee = 0.0 + est_time = 0.0 + slippage = 0.0 + + # Fee: try several possible field names + for key in ("bridgeFee", "fee", "gasFee", "totalFee", "estimatedFee"): + if key in route: + try: + fee = float(route[key]) + except (ValueError, TypeError): + pass + break + + # Estimated time in minutes + for key in ("estimatedTime", "time", "bridgeTime", "eta"): + if key in route: + try: + raw = float(route[key]) + # If value looks like seconds (> 300), convert to minutes + est_time = raw / 60.0 if raw > 300 else raw + except (ValueError, TypeError): + pass + break + + # Slippage percentage + for key in ("slippage", "priceImpact", "estimatedSlippage"): + if key in route: + try: + slippage = abs(float(route[key])) + except (ValueError, TypeError): + pass + break + + return fee, est_time, slippage + + +def rank_routes(quotes): + """Rank bridge routes by composite score. + + Args: + quotes: list of route dicts from onchainos swap quote + + Returns: + list of dicts with original fields + score, sorted best-first + """ + if not quotes: + return [] + + fees = [] + times = [] + slippages = [] + + for q in quotes: + f, t, s = extract_route_fields(q) + fees.append(f) + times.append(t) + slippages.append(s) + + fee_scores = normalize(fees, lower_is_better=True) + time_scores = normalize(times, lower_is_better=True) + slip_scores = normalize(slippages, lower_is_better=True) + + results = [] + for i, q in enumerate(quotes): + composite = ( + W_FEE * fee_scores[i] + + W_TIME * time_scores[i] + + W_SLIPPAGE * slip_scores[i] + ) + results.append({ + "route": q.get("routeName", q.get("bridge", q.get("protocol", f"Route-{i+1}"))), + "fee_usd": round(fees[i], 4), + "est_time_min": round(times[i], 1), + "slippage_pct": round(slippages[i], 4), + "score": round(composite, 2), + "raw": q, + }) + + results.sort(key=lambda r: r["score"], reverse=True) + return results + + +def main(): + parser = argparse.ArgumentParser(description="Rank bridge routes by cost/speed/slippage") + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("--quotes", type=str, help="JSON string of quote array") + group.add_argument("--file", type=str, help="Path to JSON file with quotes") + args = parser.parse_args() + + if args.file: + with open(args.file, "r", encoding="utf-8") as f: + data = json.load(f) + else: + data = json.loads(args.quotes) + + # Accept both single route and array + if isinstance(data, dict): + if "routes" in data: + routes = data["routes"] + elif "data" in data: + routes = data["data"] if isinstance(data["data"], list) else [data["data"]] + else: + routes = [data] + else: + routes = data + + ranked = rank_routes(routes) + + print(json.dumps(ranked, indent=2, ensure_ascii=False)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/skills/xlayer-bridge-yield/scripts/position_monitor.py b/skills/xlayer-bridge-yield/scripts/position_monitor.py new file mode 100644 index 000000000..fd5e05ea1 --- /dev/null +++ b/skills/xlayer-bridge-yield/scripts/position_monitor.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +"""Position monitor for xlayer-bridge-yield plugin. + +Tracks yield farming positions on X Layer, calculates P/L, +and generates alerts for depeg events, stop-loss triggers, +and APY changes. + +Usage: + python3 position_monitor.py --balances '' --prices '' + python3 position_monitor.py --file positions.json + +Output: JSON with position summary and active alerts. +""" + +import argparse +import json +import sys + +# Alert thresholds (defaults) +DEPEG_THRESHOLD = 0.005 # 0.5% deviation from $1.00 +STOP_LOSS_PCT = -5.0 # percent +APY_DROP_THRESHOLD = 2.0 # percentage points + +# Known stablecoins (for depeg detection) +STABLECOINS = { + "usdc", "usdt", "dai", "tusd", "busd", "frax", + "usd coin", "tether", "dai stablecoin", +} + + +def is_stablecoin(symbol, name=""): + """Check if a token is a stablecoin by symbol or name.""" + sym = symbol.lower().strip() + nm = name.lower().strip() + return sym in STABLECOINS or any(s in nm for s in STABLECOINS) + + +def check_depeg(price, threshold=DEPEG_THRESHOLD): + """Check if a stablecoin has depegged beyond threshold.""" + if price <= 0: + return True, 1.0 + deviation = abs(price - 1.0) + return deviation > threshold, deviation + + +def analyze_positions(balances, prices, stop_loss_pct=None, depeg_threshold=None): + """Analyze positions and generate alerts. + + Args: + balances: list of balance records from onchainos portfolio + prices: list/dict of price records from onchainos market price + stop_loss_pct: override stop-loss threshold (default: STOP_LOSS_PCT) + depeg_threshold: override depeg threshold (default: DEPEG_THRESHOLD) + + Returns: + dict with positions list and alerts list + """ + sl_pct = stop_loss_pct if stop_loss_pct is not None else STOP_LOSS_PCT + dp_thresh = depeg_threshold if depeg_threshold is not None else DEPEG_THRESHOLD + # Build price lookup + price_map = {} + if isinstance(prices, list): + for p in prices: + addr = p.get("address", p.get("tokenAddress", "")).lower() + sym = p.get("symbol", p.get("tokenSymbol", "")).lower() + price_val = 0.0 + for k in ("price", "currentPrice", "usdPrice", "lastPrice"): + if k in p: + try: + price_val = float(p[k]) + except (ValueError, TypeError): + pass + break + if addr: + price_map[addr] = price_val + if sym: + price_map[sym] = price_val + elif isinstance(prices, dict): + for key, val in prices.items(): + try: + price_map[key.lower()] = float(val) + except (ValueError, TypeError): + if isinstance(val, dict): + for k in ("price", "currentPrice", "usdPrice"): + if k in val: + try: + price_map[key.lower()] = float(val[k]) + except (ValueError, TypeError): + pass + break + + positions = [] + alerts = [] + + # Process balances + balance_list = balances if isinstance(balances, list) else [balances] + + for bal in balance_list: + symbol = bal.get("symbol", bal.get("tokenSymbol", "???")) + name = bal.get("name", bal.get("tokenName", "")) + addr = bal.get("address", bal.get("tokenAddress", "")).lower() + amount = 0.0 + for k in ("balance", "amount", "tokenAmount", "holdingAmount"): + if k in bal: + try: + amount = float(bal[k]) + except (ValueError, TypeError): + pass + break + + # Skip zero/dust balances + if amount < 0.001: + continue + + # Get current price + price = price_map.get(addr, price_map.get(symbol.lower(), 0.0)) + value = amount * price if price > 0 else 0.0 + + # Determine status + status = "HEALTHY" + is_stable = is_stablecoin(symbol, name) + + if is_stable and price > 0: + depegged, deviation = check_depeg(price, dp_thresh) + if depegged: + status = "DEPEG_WARNING" + alerts.append({ + "type": "DEPEG", + "severity": "HIGH" if deviation > 0.02 else "MEDIUM", + "token": symbol, + "message": f"{symbol} price ${price:.4f} deviates {deviation*100:.2f}% from $1.00 peg", + "action": "Consider exiting position or monitoring closely", + }) + + # Entry price tracking (from balance metadata if available) + entry_price = None + for k in ("entryPrice", "avgCost", "costBasis"): + if k in bal: + try: + entry_price = float(bal[k]) + except (ValueError, TypeError): + pass + break + + change_pct = None + change_usd = None + if entry_price and entry_price > 0 and price > 0: + change_pct = ((price - entry_price) / entry_price) * 100 + change_usd = (price - entry_price) * amount + + if change_pct <= sl_pct: + status = "STOP_LOSS" + alerts.append({ + "type": "STOP_LOSS", + "severity": "CRITICAL", + "token": symbol, + "message": f"{symbol} position down {change_pct:.1f}% (threshold: {sl_pct}%)", + "action": "Consider exiting position to limit losses", + }) + + positions.append({ + "token": symbol, + "name": name, + "amount": round(amount, 6), + "price_usd": round(price, 4) if price > 0 else None, + "value_usd": round(value, 2) if value > 0 else None, + "change_pct": round(change_pct, 2) if change_pct is not None else None, + "change_usd": round(change_usd, 2) if change_usd is not None else None, + "status": status, + "is_stablecoin": is_stable, + }) + + # Sort: alerts first, then by value descending + status_order = {"STOP_LOSS": 0, "DEPEG_WARNING": 1, "HEALTHY": 2} + positions.sort(key=lambda p: (status_order.get(p["status"], 9), -(p["value_usd"] or 0))) + + total_value = sum(p["value_usd"] for p in positions if p["value_usd"]) + total_change = sum(p["change_usd"] for p in positions if p["change_usd"] is not None) + + return { + "positions": positions, + "alerts": alerts, + "summary": { + "total_value_usd": round(total_value, 2), + "total_change_usd": round(total_change, 2) if total_change else None, + "position_count": len(positions), + "alert_count": len(alerts), + }, + } + + +def main(): + parser = argparse.ArgumentParser(description="Monitor X Layer yield positions") + parser.add_argument("--balances", type=str, help="JSON balance data from onchainos portfolio") + parser.add_argument("--prices", type=str, default="{}", help="JSON price data from onchainos market price") + parser.add_argument("--file", type=str, help="Path to combined JSON file") + parser.add_argument("--stop-loss", type=float, default=STOP_LOSS_PCT, help="Stop-loss threshold (percent)") + parser.add_argument("--depeg-threshold", type=float, default=DEPEG_THRESHOLD, help="Depeg alert threshold") + args = parser.parse_args() + + if args.file: + with open(args.file, "r", encoding="utf-8") as f: + data = json.load(f) + balances = data.get("balances", []) + prices = data.get("prices", {}) + else: + if not args.balances: + print(json.dumps({"error": "Either --balances or --file is required"})) + return 1 + balances = json.loads(args.balances) + prices = json.loads(args.prices) + + result = analyze_positions(balances, prices, + stop_loss_pct=args.stop_loss, + depeg_threshold=args.depeg_threshold) + print(json.dumps(result, indent=2, ensure_ascii=False)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/skills/xlayer-bridge-yield/scripts/yield_scanner.py b/skills/xlayer-bridge-yield/scripts/yield_scanner.py new file mode 100644 index 000000000..30bc18f6f --- /dev/null +++ b/skills/xlayer-bridge-yield/scripts/yield_scanner.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +"""Yield pool scanner for xlayer-bridge-yield plugin. + +Processes token and market data from onchainos to rank DeFi yield pools +on X Layer by a risk-adjusted score. + +Usage: + python3 yield_scanner.py --tokens '' --market '' --security '' + python3 yield_scanner.py --file pool_data.json + +Output: JSON array of ranked pools with risk labels. +""" + +import argparse +import json +import sys +from datetime import datetime, timezone + +# Minimum thresholds (defaults, can be overridden) +MIN_TVL = 50_000 # USD +MIN_APY = 1.0 # percent +MIN_AGE_DAYS = 30 + +# Risk scoring weights +W_APY = 0.35 +W_TVL = 0.30 +W_SECURITY = 0.20 +W_AGE = 0.15 + + +def classify_risk(security_score, tvl, age_days): + """Classify pool risk as LOW, MEDIUM, or HIGH.""" + if security_score <= 2 or tvl < 10_000 or age_days < 7: + return "HIGH" + if security_score <= 5 or tvl < MIN_TVL or age_days < MIN_AGE_DAYS: + return "MEDIUM" + return "LOW" + + +def normalize(values, lower_is_better=False): + """Min-max normalize to [0, 10].""" + if not values: + return [] + mn, mx = min(values), max(values) + if mn == mx: + return [5.0] * len(values) + if lower_is_better: + return [10.0 * (mx - v) / (mx - mn) for v in values] + return [10.0 * (v - mn) / (mx - mn) for v in values] + + +def parse_pool_data(tokens_data, market_data, security_data): + """Merge token, market, and security data into unified pool records.""" + pools = [] + + # Build lookup maps + market_map = {} + if isinstance(market_data, list): + for m in market_data: + addr = m.get("address", m.get("tokenAddress", "")).lower() + if addr: + market_map[addr] = m + elif isinstance(market_data, dict): + addr = market_data.get("address", "").lower() + if addr: + market_map[addr] = market_data + + security_map = {} + if isinstance(security_data, list): + for s in security_data: + addr = s.get("address", s.get("tokenAddress", "")).lower() + if addr: + security_map[addr] = s + elif isinstance(security_data, dict): + addr = security_data.get("address", "").lower() + if addr: + security_map[addr] = security_data + + # Process tokens + token_list = tokens_data if isinstance(tokens_data, list) else [tokens_data] + + for token in token_list: + addr = token.get("address", token.get("tokenAddress", "")).lower() + name = token.get("name", token.get("tokenName", "Unknown")) + symbol = token.get("symbol", token.get("tokenSymbol", "???")) + + # Market data + mkt = market_map.get(addr, {}) + apy = 0.0 + tvl = 0.0 + for apy_key in ("apy", "APY", "yield", "annualReturn"): + if apy_key in mkt: + try: + apy = float(mkt[apy_key]) + except (ValueError, TypeError): + pass + break + for tvl_key in ("tvl", "TVL", "totalValueLocked", "liquidity"): + if tvl_key in mkt: + try: + tvl = float(mkt[tvl_key]) + except (ValueError, TypeError): + pass + break + + # Security data + sec = security_map.get(addr, {}) + sec_score = 5 # default: neutral + for sec_key in ("riskScore", "score", "securityScore", "riskLevel"): + if sec_key in sec: + raw = sec[sec_key] + if isinstance(raw, str): + risk_map = {"low": 8, "medium": 5, "high": 2, "critical": 0} + sec_score = risk_map.get(raw.lower(), 5) + else: + try: + sec_score = int(raw) + except (ValueError, TypeError): + pass + break + + # Pool age + age_days = 365 # default: assume established + for age_key in ("createdAt", "launchDate", "deployedAt"): + if age_key in token: + try: + ts = token[age_key] + if isinstance(ts, (int, float)): + created = datetime.fromtimestamp(ts, tz=timezone.utc) + else: + created = datetime.fromisoformat(str(ts).replace("Z", "+00:00")) + age_days = (datetime.now(timezone.utc) - created).days + except (ValueError, TypeError, OSError): + pass + break + + pool = { + "address": addr, + "name": name, + "symbol": symbol, + "apy": apy, + "tvl": tvl, + "security_score": sec_score, + "age_days": age_days, + "risk": classify_risk(sec_score, tvl, age_days), + } + pools.append(pool) + + return pools + + +def rank_pools(pools, min_tvl=MIN_TVL, min_apy=MIN_APY): + """Filter and rank pools by risk-adjusted composite score.""" + # Filter + filtered = [ + p for p in pools + if p["tvl"] >= min_tvl + and p["apy"] >= min_apy + and p["risk"] != "CRITICAL" + ] + + if not filtered: + return [] + + apys = [p["apy"] for p in filtered] + tvls = [p["tvl"] for p in filtered] + secs = [p["security_score"] for p in filtered] + ages = [p["age_days"] for p in filtered] + + apy_scores = normalize(apys, lower_is_better=False) + tvl_scores = normalize(tvls, lower_is_better=False) + sec_scores = normalize(secs, lower_is_better=False) + age_scores = normalize(ages, lower_is_better=False) + + results = [] + for i, p in enumerate(filtered): + composite = ( + W_APY * apy_scores[i] + + W_TVL * tvl_scores[i] + + W_SECURITY * sec_scores[i] + + W_AGE * age_scores[i] + ) + results.append({ + "pool": f"{p['symbol']} ({p['name']})", + "address": p["address"], + "apy_pct": round(p["apy"], 2), + "tvl_usd": round(p["tvl"], 0), + "risk": p["risk"], + "security_score": p["security_score"], + "age_days": p["age_days"], + "score": round(composite, 2), + }) + + results.sort(key=lambda r: r["score"], reverse=True) + return results + + +def main(): + parser = argparse.ArgumentParser(description="Scan and rank X Layer yield pools") + parser.add_argument("--tokens", type=str, help="JSON token data from onchainos token search") + parser.add_argument("--market", type=str, default="{}", help="JSON market data from onchainos market price") + parser.add_argument("--security", type=str, default="{}", help="JSON security data from onchainos security token-scan") + parser.add_argument("--file", type=str, help="Path to combined JSON file") + parser.add_argument("--min-tvl", type=float, default=MIN_TVL, help="Minimum TVL filter (USD)") + parser.add_argument("--min-apy", type=float, default=MIN_APY, help="Minimum APY filter (percent)") + args = parser.parse_args() + + if args.file: + with open(args.file, "r", encoding="utf-8") as f: + data = json.load(f) + tokens_data = data.get("tokens", []) + market_data = data.get("market", {}) + security_data = data.get("security", {}) + else: + if not args.tokens: + print(json.dumps({"error": "Either --tokens or --file is required"})) + return 1 + tokens_data = json.loads(args.tokens) + market_data = json.loads(args.market) + security_data = json.loads(args.security) + + pools = parse_pool_data(tokens_data, market_data, security_data) + ranked = rank_pools(pools, min_tvl=args.min_tvl, min_apy=args.min_apy) + + print(json.dumps(ranked, indent=2, ensure_ascii=False)) + return 0 + + +if __name__ == "__main__": + sys.exit(main())