From 7ea06e99d6f0199c302e0c5242c860c92e210aaa Mon Sep 17 00:00:00 2001 From: Ishita Agarwal Date: Tue, 16 Jun 2026 20:01:09 +0530 Subject: [PATCH 01/23] feat: add yield-agentkit-builder skill --- .../.codex-plugin/plugin.json | 43 ++ .../skills/yield-agentkit-builder/README.md | 120 ++++++ .../skills/yield-agentkit-builder/SKILL.md | 368 ++++++++++++++++++ .../references/api-field-mapping.md | 116 ++++++ .../references/common-pitfalls.md | 303 ++++++++++++++ .../references/integration-patterns.md | 139 +++++++ .../references/mcp-tools.md | 136 +++++++ .../references/output-formats.md | 68 ++++ .../references/policies.md | 44 +++ .../references/setup.md | 100 +++++ .../references/signing-patterns.md | 156 ++++++++ 11 files changed, 1593 insertions(+) create mode 100644 yield-agentkit-plugin/.codex-plugin/plugin.json create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/README.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md diff --git a/yield-agentkit-plugin/.codex-plugin/plugin.json b/yield-agentkit-plugin/.codex-plugin/plugin.json new file mode 100644 index 0000000..8dbc94a --- /dev/null +++ b/yield-agentkit-plugin/.codex-plugin/plugin.json @@ -0,0 +1,43 @@ +{ + "name": "yield-agentkit-plugin", + "version": "1.0.0", + "description": "Discover and manage on-chain yield opportunities using the Yield.xyz API.", + "author": { + "name": "Yield.xyz", + "url": "https://yield.xyz" + }, + "homepage": "https://docs.yield.xyz", + "repository": "https://github.com/stakekit/agentkit", + "license": "MIT", + "keywords": [ + "defi", + "yield", + "staking", + "lending", + "web3", + "ethereum", + "solana" + ], + "skills": "./yield-agentkit", + "mcpServers": "./.mcp.json", + "interface": { + "displayName": "Yield.xyz AgentKit", + "shortDescription": "Find, compare, enter, exit, and manage DeFi yield positions.", + "longDescription": "Yield.xyz AgentKit connects Codex to the Yield.xyz MCP server for discovering yield opportunities, checking balances, and preparing unsigned transactions for staking, lending, vaults, restaking, and liquidity pools across supported networks.", + "category": "Finance", + "capabilities": [ + "Interactive", + "Read", + "Write" + ], + "websiteURL": "https://yield.xyz", + "privacyPolicyURL": "https://docs.yield.xyz/docs/privacy-policy", + "termsOfServiceURL": "https://docs.yield.xyz/docs/terms-of-use", + "defaultPrompt": [ + "Show me top USDC yields on Base.", + "Compare ETH staking yields on Ethereum.", + "Check my yield balances for this wallet." + ], + "brandColor": "#FFC21B" + } + } \ No newline at end of file diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md new file mode 100644 index 0000000..7698421 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md @@ -0,0 +1,120 @@ +# Yield.xyz AgentKit Builder Skill + +[![AI Agent Skill](https://img.shields.io/badge/AI%20Agent-Skill-orange)](https://yield.xyz) +[![Version](https://img.shields.io/badge/version-1.0.0-blue)](https://github.com/stakekit/agentkit) + +> **Build DeFi yield apps with AI agents.** This skill teaches any MCP-compatible AI agent how to generate production-ready code that integrates with the Yield.xyz API — staking, lending, vaults, and yield across 80+ networks. + +The skill works alongside the [Yield.xyz AgentKit MCP server](https://mcp.yield.xyz/mcp), which provides live API access for looking up yield schemas and field definitions during code generation. + +--- + +## What it does + +Once installed, the agent activates this skill when you ask to build something: + +- "Build me a staking dashboard with Next.js" +- "Integrate USDC lending into my app" +- "Generate a backend service that enters yield positions" +- "Set up a neobank with DeFi yield features" +- "How do I sign Yield.xyz transactions with MetaMask?" + +The agent will generate code that calls the Yield.xyz REST API directly, using correct field names, proper transaction signing, and the full submit-hash lifecycle. + +--- + +## How it differs from other skills + +| Skill | Purpose | Output | +|---|---|---| +| `yield-agentkit` | Explore yields conversationally | Tables, summaries, portfolio views | +| `yield-agentkit-moonpay` | Enter yields end-to-end via MoonPay | Signed & broadcast transactions | +| `yield-agentkit-privy` | Enter yields end-to-end via Privy | Signed & broadcast transactions | +| **`yield-agentkit-builder`** | **Build apps that integrate Yield.xyz** | **Production-ready code** | + +The explore/execute skills use MCP tools directly. The builder skill uses MCP tools for research but generates code that calls the REST API with the user's own API key. + +--- + +## Requirements + +- An MCP-compatible AI agent (Claude Code, Codex, Gemini CLI, etc.) +- A Yield.xyz API key (get one at https://yield.xyz) + +--- + +## Install + +Open your terminal and run the command: + +``` +npx skills add stakekit/agentkit --skill yield-agentkit-builder +``` + +Choose `yield-agentkit-builder` skill from the options. + +Open your agent and say: + +``` +Set up the yield-agentkit-builder skill +``` + +The agent will read `references/setup.md` and automatically: +1. Check if the Yield.xyz AgentKit MCP is registered, and register it if not +2. Confirm the skill is loaded and ready + +--- + +## Test it + +Once installed, try: + +``` +Generate a TypeScript function that finds the best USDC yield on Base and deposits 100 USDC +``` + +``` +Build a React component that shows a user's yield portfolio +``` + +``` +How do I handle MetaMask transaction signing with Yield.xyz? +``` + +The agent will look up the live API spec via MCP, then generate code using `https://api.yield.xyz` with correct field names. + +--- + +## Folder structure + +``` +yield-agentkit-builder/ +├── SKILL.md # Main skill instructions +├── README.md # This file +└── references/ + ├── setup.md # Prerequisites and API key setup + ├── common-pitfalls.md # Known errors and how to avoid them + ├── api-field-mapping.md # How to look up endpoints and schemas from docs.json + ├── signing-patterns.md # Wallet SDKs and signing guidance per chain + ├── integration-patterns.md # Architecture per product type (custody, wallet, neobank, etc.) + ├── output-formats.md # Display rules for generated UI code + └── policies.md # API rate limits, caching, best practices +``` + +--- + +## Key references + +| Reference | What's in it | +|---|---| +| `common-pitfalls.md` | 12 real errors from builder sessions — wrong URLs, field names, gas issues, etc. | +| `signing-patterns.md` | Recommended SDKs for MetaMask, Phantom, WalletConnect, Rainbow, Coinbase, Solana, Cosmos | +| `integration-patterns.md` | Architecture for custody, wallet, neobank, aggregator, enterprise, mobile | + +--- + +## Related + +- [Yield.xyz AgentKit Skill](../yield-agentkit/README.md) — explore yields conversationally +- [Yield.xyz AgentKit MCP Server](https://mcp.yield.xyz/mcp) — the live tools +- [Yield.xyz Docs](https://docs.yield.xyz/docs/getting-started) — official documentation diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md new file mode 100644 index 0000000..3b4f888 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -0,0 +1,368 @@ +--- +name: yield-agentkit-builder +description: Build applications that integrate with the Yield.xyz API. Generates production-ready code for staking, lending, vaults, and DeFi yield across 80+ networks. Covers REST API integration, transaction signing, wallet connection, and fee monetization. Use when user wants to build an app, integrate yield, generate code, or set up a project using Yield.xyz. +metadata: + author: Yield.xyz + version: "1.0.0" + mcp-server: yield-agentkit +--- + +# Yield.xyz AgentKit — Builder Skill + +This skill helps developers build applications that integrate with the Yield.xyz API. +It generates production-ready code, guides architecture decisions, and ensures correct +API usage across all supported chains and protocols. + +--- + +## When This Skill Activates + +This skill is for **building apps** — not for exploring yields conversationally. + +Activate when the user says things like: +- "Build me a staking app" +- "Integrate Yield.xyz into my project" +- "Generate code to deposit USDC into Aave" +- "How do I call the Yield.xyz API from my backend?" +- "Set up a neobank with yield features" +- "Add yield to my wallet app" + +If the user is just exploring yields ("What's the best USDC yield on Base?"), that's +the `yield-agentkit` skill — not this one. + +--- + +## Critical Rules + +### 1. API Base URL + +**`https://api.yield.xyz`** — this is the only correct production URL. + +Do not use `api.stakek.it`, `api.stakekit.io`, or any other legacy domain. +Every code sample, fetch call, and SDK config must use `api.yield.xyz`. + +### 2. API Key Requirement + +Every integration requires the user's **own Yield.xyz API key**. + +**Before generating any integration code, ask the user for their API key.** +If they don't have one, direct them to https://dashboard.yield.xyz/login to create a project and get a key. + +Once you have the key: +- Use it in all generated code (`x-api-key` header or SDK `apiKey` config) +- Never hardcode the demo key into the user's codebase +- Recommend storing it in an environment variable (e.g., `YIELD_API_KEY`) + +### 3. Never Use MCP Action Tools — Always Use the REST API Directly + +**Do NOT call MCP action tools** (`yields_get_all`, `yields_get`, `yields_get_validators`, +`yields_get_balances`, `actions_enter`, `actions_exit`, `actions_manage`) to look up data +during a build session. The MCP returns trimmed/slimmed responses that omit fields present +in the full API. If you build code based on MCP responses, it will be wrong. + +**Instead:** Use the user's API key to call the REST API directly +(`https://api.yield.xyz/v1/...`) via `curl`, `fetch`, or any HTTP client. This gives you +the full, unmodified response — use that as the reference for building the integration. + +**The one exception:** The `yield_get_api_spec` doc tool is safe to use. It fetches +the live OpenAPI spec from `https://api.yield.xyz/docs.json`, which is always complete +and up-to-date. Use it to look up endpoint schemas, field names, and constraints. + +> 📖 **For the full list of MCP tools — which are safe to use in builder sessions +> (doc tools) and which must NOT be called (action tools) — see +> [`references/mcp-tools.md`](./references/mcp-tools.md).** Consult that file any +> time you're unsure whether a given MCP tool is appropriate for a build task. + +### 4. Never Hardcode Field Names — Always Fetch from the Live Spec + +The API evolves continuously. Do not rely on hardcoded field names or schemas from this +skill's reference files. Instead: + +1. **Fetch the live OpenAPI spec** from `https://api.yield.xyz/docs.json` (or use the + `yield_get_api_spec` tool) to discover current field names, types, and constraints +2. **Call the actual API endpoint** with the user's key to see the real response shape +3. **Then generate code** based on what the live spec and actual responses show + +This ensures generated code always matches the current API, even after breaking changes. + +### 5. Never Modify Unsigned Transactions + +Generated code must NEVER modify `unsignedTransaction` returned by the API. +Not addresses, amounts, fees, encoding — nothing. + +Amount wrong? Call the action endpoint again with the corrected amount. +Gas insufficient? Ask user to add funds, call again. + +**Modifying `unsignedTransaction` will result in permanent loss of funds.** + +### 6. Never Kill a Port Without Asking + +When running the generated project locally (dev server, preview, etc.), you may hit a +port conflict because the user already has something running on that port. **Do not +automatically kill the process on that port.** The user may have another project, +service, or tool intentionally bound to it — killing it without consent can disrupt +their unrelated work. + +**Correct order of actions:** + +1. **Try the default port first** (e.g. `3000` for most Node apps, `5173` for Vite, + `8000` for Python). If it's free, use it. +2. **If the default port is in use, try the next common free port** — `3001`, `3002`, + `5001`, `5173`, `8080`, `8081`, etc. Most frameworks accept a `PORT` env var or + `--port` flag (e.g. `PORT=3001 npm run dev`, `vite --port 5174`). +3. **Only if no reasonable alternative port is available** (or the framework can't + easily be rebound), **ask the user first** before killing anything: + > "Port 3000 is in use by another process and I couldn't find a free alternative. + > Would you like me to stop the process on 3000, or do you want to free it yourself?" +4. **Kill the port only after the user explicitly agrees.** + +This rule applies to every port-binding step: dev servers, preview tools, local +databases, mock services, etc. + +### 7. Only Install Verified, Widely-Used Dependencies + +Every dependency you add to the user's project is a supply-chain risk. Malicious or +typosquatted npm packages are a real attack vector — a single unverified `postinstall` +script can exfiltrate the user's `YIELD_API_KEY`, RPC keys, or wallet seed. + +**Default to the smallest possible dependency set.** Before `npm install`-ing +anything, apply this checklist: + +1. **Prefer the standard library / built-in APIs.** Modern Node and browsers cover + `fetch`, `crypto`, `URL`, `AbortController`, etc. — don't pull in `axios`, + `node-fetch`, `uuid`, etc. unless there's a concrete reason. +2. **Prefer packages already implied by the stack.** If the user is on Next.js, + use what Next ships. If on Vite + React, use the canonical ecosystem picks + (`@tanstack/react-query`, `zod`, `viem`, `wagmi`, etc.). +3. **Before installing any package the user hasn't mentioned, verify it on npm:** + - Published by a known, trusted maintainer or org (e.g. `@yieldxyz`, `wevm`, + `TanStack`, `solana-labs`, `cosmos`, `ethers`, `coinbase`, `metamask`, etc.) + - **High weekly download count** (rule of thumb: ≥ 100k/week for general-purpose + libs; lower is acceptable only for narrow niche packages with a clear maintainer) + - Recently maintained (release within the last 6–12 months) + - Has a GitHub repo, README, and no open security advisories + - Name matches exactly — watch for typosquats (`axois`, `requst`, `lodahs`, etc.) +4. **Only widely-used, verified packages install silently.** If a package fails any + of the checks above — obscure, low downloads, unknown maintainer, recently + renamed, unfamiliar — **stop and ask the user before installing**: + + > "The cleanest way to do X is the `foo-bar` package, but it only has ~2k + > weekly downloads and I'm not familiar with the maintainer. Want me to install + > it, pick a more popular alternative (`baz-qux`, 500k weekly downloads), or + > implement X inline without a dependency?" + +5. **Never install a package just because the user vaguely asked for functionality + it provides.** If a lightweight inline implementation (~30 lines) replaces a + sketchy dependency, write the inline version. + +This rule applies to both frontend and backend — `package.json`, `requirements.txt`, +`go.mod`, `Cargo.toml`, and any other dependency manifest. + +### 8. Always Include Pagination When Listing Yields + +Yield.xyz exposes **2,900+ yields across 80+ networks**. Popular chains like +Ethereum, Base, and Arbitrum each have hundreds of entries. A UI that renders the +full list in one page is unusable — slow to load, painful to scroll, and expensive +to re-render. + +**Pagination is a default, not a feature request.** Do not wait for the user to +ask. Every generated app that lists yields, balances, validators, or transactions +must ship with pagination wired in from the start. + +**Baseline requirements for any list view:** + +1. **Page size** — default to 20–50 items per page; never render the full list at once. +2. **Use the API's built-in pagination params** on `GET /v1/yields` (`limit`, + `offset` or cursor — check `yield_get_api_spec` for the current shape). Do + **not** fetch everything client-side and slice — that defeats the point. +3. **Provide a way to change pages** — next/prev buttons, numbered pager, or an + infinite-scroll sentinel (whichever fits the UI). +4. **Preserve filters across pages** — network, token, type, provider filters + must remain applied when paging. +5. **Show total count** (e.g. "1-20 of 1,847 yields") when the API returns it, so + the user understands the scale. +6. **Same rule for validators and balances** — validator lists (`yields_get_validators`) + and position lists (`yields_get_balances`) can also be long; paginate them too. +7. **Prefer server-side sort, search, and filter.** Before writing any `.sort()`, + `.filter()`, or `.toLowerCase().includes(...)` on the client, check the live + OpenAPI spec (`yield_get_api_spec({ endpoint: "/v1/yields" })` or + `curl https://api.yield.xyz/docs.json | jq '.paths["/v1/yields"]'`) for + `sort`, `search`, and filter query params on the endpoint. The `/v1/yields` + endpoint in particular supports server-side sorting (by APY, TVL, etc.) and + text search via query params. Pass those params through — don't fetch-all + and sort locally. A client-side sort across 3,000 yields both defeats the + pagination rule above and produces wrong results across pages (page 2 + sorted by APY on the client means two different sort contexts). + If a param you need really doesn't exist, fall back to client-side — but + that's a last resort, not a default. + +The user can always adjust page size or swap to infinite scroll later. The +non-negotiable is that **the first version you deliver must not render an +unbounded list**, and any sort / search / filter must go through the API +whenever the API supports it. + +--- + +## Workflow + +### Step 0 — Register the Yield.xyz MCP Server (do this FIRST, automatically) + +**The very first action in every builder session** — before asking for an API key, +before asking what the user is building — is to ensure the `yield-agentkit` MCP +server is registered with the user's agent. This is **not optional**. The skill's +doc tools (`yield_get_api_spec`, `yield_get_chain_guide`, +`yield_get_transaction_guide`, etc.) come from that MCP server, and without them the +skill will generate code from memory rather than from the live spec. + +**Do this automatically — do not ask the user to run the command themselves.** +Run the appropriate registration command for their agent, then verify it connected. + +For Claude Code: +```bash +claude mcp add yield-agentkit --transport http https://mcp.yield.xyz/mcp +claude mcp list # verify "yield-agentkit" shows "✓ Connected" +``` + +For other agents (Codex, Gemini CLI, etc.), write the `yield-agentkit` entry into +the appropriate MCP config file (`~/.mcp.json` or project-local `.mcp.json`). + +If registration fails, stop and surface the error to the user — do not attempt to +build anything until the MCP is connected. Full details and config snippets are in +[`references/setup.md`](./references/setup.md). + +### Step 1 — Understand the Use Case + +Ask the user what they're building. The answer determines architecture, signing approach, +and which reference files to load. + +| Product Type | Signing | Key Reference | +|---|---|---| +| Custody platform | Server-side (HSM/KMS) | `references/signing-patterns.md` | +| Consumer wallet | Client-side (browser wallet) | `references/signing-patterns.md` | +| Neobank / fintech | Server-side + fee monetization | `references/integration-patterns.md` | +| Yield aggregator | Server-side + yield discovery | `references/integration-patterns.md` | +| Mobile app | WalletConnect or embedded wallet | `references/signing-patterns.md` | +| Backend service | Server-side | `references/signing-patterns.md` | + +See **[`references/integration-patterns.md`](./references/integration-patterns.md)** for +architecture diagrams and patterns per product type. + +### Step 2 — Look Up the API Spec + +Before generating any code, fetch the live OpenAPI spec to discover current field names +and request/response shapes: + +**Option A — Use the doc tool:** +``` +yield_get_api_spec({ endpoint: "/v1/actions/enter", section: "endpoints" }) +``` + +**Option B — Fetch directly with the user's key:** +```bash +curl https://api.yield.xyz/docs.json | jq '.paths["/v1/actions/enter"]' +``` + +Use the spec as the source of truth. Never assume field names from memory. + +### Step 3 — Call the Real API to See Actual Responses + +Use the user's API key to make a real API call and inspect the response. This lets you +see the actual field names, nesting, and data types in the response: + +```bash +curl -s "https://api.yield.xyz/v1/yields?network=base&token=USDC&limit=1" \ + -H "x-api-key: $YIELD_API_KEY" | jq . +``` + +Use this real response as the reference when building the frontend or backend integration. + +### Step 4 — Generate Code + +Generate code that: +1. Uses `https://api.yield.xyz` as the base URL +2. Passes the user's API key via `x-api-key` header +3. Uses field names exactly as seen in the live spec and API responses +4. Handles the full transaction lifecycle (action -> sign -> broadcast -> submit-hash) +5. Follows the signing pattern appropriate for the user's product type and wallet choice + +See **[`references/signing-patterns.md`](./references/signing-patterns.md)** for +wallet SDK references and signing guidance per chain. + +### Step 5 — Run the Project Yourself and Report URLs + +**Do not tell the user "now run `npm run dev`" and walk away.** The skill's job +isn't done until the project is actually running and you've handed the user the +live URLs. Run every step yourself: + +1. **Install dependencies yourself** — `pnpm install` / `npm install` / `yarn install` + based on the lockfile you generated. Surface install errors and fix them. +2. **Verify required env vars are set** — `YIELD_API_KEY`, RPC URLs, any wallet + secrets. If something is missing, ask the user for it and write it into `.env`. +3. **Start the servers yourself** — run the dev command(s) for every service the + project exposes (backend API, frontend, worker, etc.). For multi-service projects, + start each one (in the background if needed) and wait until each is healthy. +4. **Respect the port rules** (see Critical Rule #6) — try the default port, fall + back to a nearby free port, and only ask the user before killing an occupied + port as a last resort. +5. **Report every useful URL back to the user** in a single clean summary. For + example: + + ``` + ✅ Project is running. + + Frontend: http://localhost:3000 + Backend API: http://localhost:3001 + API health: http://localhost:3001/health + ``` + + Include whichever URLs apply — frontend, backend, admin panel, WebSocket endpoint, + any auth callback URLs, block explorer link for the test chain, etc. + +6. **Do a quick smoke test yourself** — hit the backend health endpoint, load the + frontend, verify the Yield.xyz call returns data. Only then hand over to the user + with the URL list above. If anything failed, fix it first; don't hand over a + broken project. + +Only after the above is green should you suggest a test transaction (small amount, +low-value chain) and point the user at the block explorer to verify it. + +--- + +## Common Pitfalls + +**Before generating any code, read [`references/common-pitfalls.md`](./references/common-pitfalls.md).** + +This file documents real errors encountered during builder sessions — wrong API URLs, +incorrect field names, browser wallet gas issues, and more. Every pitfall listed there +has caused real failures. Avoid them. + +--- + +## Reference Files + +Read these on demand when generating code. **Always read the relevant reference +before generating code for that topic.** + +| File | When to Read | +|---|---| +| **[`references/mcp-tools.md`](./references/mcp-tools.md)** | **Before invoking any MCP tool** — which tools are safe (doc tools) vs. which must never be called (action tools) | +| **[`references/common-pitfalls.md`](./references/common-pitfalls.md)** | **Before generating any code** — known errors and how to avoid them | +| **[`references/signing-patterns.md`](./references/signing-patterns.md)** | Before generating signing/wallet code — SDKs, libraries, chain-specific guidance | +| **[`references/integration-patterns.md`](./references/integration-patterns.md)** | When user describes their product type — architecture per use case | +| **[`references/output-formats.md`](./references/output-formats.md)** | When generating UI code — display rules, number formatting | +| **[`references/policies.md`](./references/policies.md)** | API usage limits, rate limiting, caching guidance | +| **[`references/setup.md`](./references/setup.md)** | First-time setup — prerequisites | + +--- + +## SDK Option + +For TypeScript/JavaScript projects, the `@yieldxyz/sdk` package wraps the REST API: + +```typescript +import { Sdk } from "@yieldxyz/sdk"; +const sdk = new Sdk({ apiKey: process.env.YIELD_API_KEY }); +``` + +For other languages (Python, Go, Rust), generate REST API calls directly. +Refer to `https://api.yield.xyz/docs.json` for the complete OpenAPI spec. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md new file mode 100644 index 0000000..84a0f3a --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md @@ -0,0 +1,116 @@ +# API Reference + +**API Base URL:** `https://api.yield.xyz` + +This is the only correct production URL. Do not use `api.stakek.it`, `api.stakekit.io`, +or any other legacy domain. + +--- + +## How to Look Up Field Names and Schemas + +**Never hardcode field names from this file into generated code.** The API evolves +continuously. Always verify against the live spec before generating code. + +### Option 1 — OpenAPI Spec (recommended) + +The live OpenAPI spec is at `https://api.yield.xyz/docs.json`. It contains every endpoint, +field name, type, constraint, and example. Use this as the source of truth. + +You can also use the `yield_get_api_spec` doc tool to query specific endpoints: + +``` +yield_get_api_spec({ endpoint: "/v1/actions/enter", section: "endpoints" }) +yield_get_api_spec({ query: "balances", section: "endpoints" }) +yield_get_api_spec({ section: "schemas", query: "ActionDto" }) +``` + +### Option 2 — Call the API directly + +Use the user's API key to make a real request and inspect the response: + +```bash +# See yield discovery response shape +curl -s "https://api.yield.xyz/v1/yields?network=base&token=USDC&limit=1" \ + -H "x-api-key: $YIELD_API_KEY" | jq . + +# See action response shape +curl -s -X POST "https://api.yield.xyz/v1/actions/enter" \ + -H "x-api-key: $YIELD_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"yieldId":"...","address":"...","arguments":{"amount":"1"}}' | jq . +``` + +--- + +## Endpoint Reference + +These are the API paths. For exact request/response schemas, always check `docs.json`. + +### Yield Discovery + +| Endpoint | Method | Purpose | +|---|---|---| +| `/v1/yields` | GET | List/filter yield opportunities | +| `/v1/yields/{yieldId}` | GET | Full metadata for a specific yield | +| `/v1/yields/{yieldId}/validators` | GET | Validators for delegation-based yields | +| `/v1/yields/balances` | POST | Positions and pending actions for a wallet | +| `/v1/networks` | GET | List all supported networks | + +### Actions + +| Endpoint | Method | Purpose | +|---|---|---| +| `/v1/actions/enter` | POST | Build enter (deposit/stake) transactions | +| `/v1/actions/exit` | POST | Build exit (withdraw/unstake) transactions | +| `/v1/actions/manage` | POST | Build manage (claim/restake) transactions | +| `/v1/transactions/{txId}/submit-hash` | PUT | Report broadcast hash (mandatory after every tx) | +| `/v1/transactions/{txId}` | GET | Check transaction status | + +--- + +## Key Principles + +These principles are stable across API versions: + +1. **Amounts are human-readable strings.** `"100"` means 100 USDC, not 100 wei. The API + handles decimal conversion internally. Never convert to wei or raw integers. + +2. **Always read the yield schema before calling an action.** Call `GET /v1/yields/{yieldId}` + and inspect the `mechanics.arguments` field to discover exactly what the action endpoint + requires. Each yield is different. + +3. **Submit hash after every broadcast.** Call `PUT /v1/transactions/{txId}/submit-hash` + with the on-chain transaction hash after broadcasting. Without this, positions and + balances won't update. + +4. **Execute transactions in `stepIndex` order.** An action may return multiple transactions + (e.g., approve + deposit). Execute them sequentially by `stepIndex`, waiting for on-chain + confirmation between each. + +5. **Never modify `unsignedTransaction`.** Sign it exactly as returned. If something is wrong, + create a new action — don't edit the existing one. + +--- + +## Headers + +All API calls require: + +``` +Content-Type: application/json +x-api-key: YOUR_API_KEY +``` + +--- + +## Common Error Status Codes + +| Code | Meaning | +|---|---| +| `400` | Bad input — check field names, required fields, amount format | +| `401` | Missing or invalid API key | +| `404` | Yield or transaction not found | +| `412` | Precondition failed — yield not available, below minimum, etc. | +| `429` | Rate limited — respect `retry-after` header | +| `503` | Upstream service unavailable — retry with backoff | diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md new file mode 100644 index 0000000..31d63d4 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md @@ -0,0 +1,303 @@ +# Common Pitfalls + +Real errors encountered during builder sessions. Read this before generating any code. + +--- + +## 1. Wrong API Base URL + +**Error:** Using `api.stakek.it` or `api.stakekit.io` instead of `api.yield.xyz`. + +**What happens:** The legacy API uses completely different field names (`integrationId` +instead of `yieldId`, `addresses` instead of `address`, `args` instead of `arguments`). +Code built against the legacy API will fail silently or return 400 errors on the +correct API. + +**Fix:** Always use `https://api.yield.xyz`. No exceptions. If you see `stakek.it` or +`stakekit.io` in any documentation, URL, or code — it's outdated. Replace it. + +--- + +## 2. Wrong Field Names in Action Requests + +**Error:** Using legacy field names in request bodies. + +| Wrong (legacy) | Correct (`api.yield.xyz`) | +|---|---| +| `integrationId` | `yieldId` | +| `addresses` | `address` | +| `args` | `arguments` | +| `args.amount` | `arguments.amount` | + +**What happens:** `400 Bad Request` with no helpful error message. + +**Fix:** Always fetch the live OpenAPI spec from `https://api.yield.xyz/docs.json` +before generating code. Never rely on field names from memory or cached documentation. + +--- + +## 3. Passing Amounts in Wei + +**Error:** Converting amounts to wei/raw integers (e.g., `"100000000"` for 100 USDC). + +**What happens:** The API interprets this as 100,000,000 USDC — a massive over-deposit +that will either fail or drain the user's entire balance. + +**Fix:** Amounts are always human-readable strings. `"100"` means 100 USDC. `"1.5"` means +1.5 ETH. The API handles decimal conversion internally. + +--- + +## 4. Browser Wallet Gas Estimation Issues + +**Error:** Passing `nonce`, `type`, `chainId`, or stale gas values from the API response +to a browser wallet (MetaMask, Phantom EVM). + +**What happens:** +- MetaMask shows "This transaction is likely to fail" +- MetaMask shows "Network fee: Unavailable" +- Transaction simulates against wrong state and reverts + +**Fix:** When signing with browser wallets: +1. Always `JSON.parse()` the `unsignedTransaction` (it's a JSON string on EVM) +2. Strip `nonce`, `type`, and `chainId` — the wallet manages these +3. On L2s (Base, Arbitrum, Optimism), omit gas fields entirely — let the wallet estimate +4. On Ethereum mainnet, you can optionally pass gas fields for better UX + +See `signing-patterns.md` for the recommended wallet SDKs per wallet. + +--- + +## 5. Using `provider.waitForTransaction()` with Browser Providers + +**Error:** Calling `provider.waitForTransaction(hash)` with an injected browser provider. + +**What happens:** The call hangs indefinitely and never resolves. This is a known issue +with MetaMask and Phantom's injected providers. + +**Fix:** Use manual receipt polling instead: +```typescript +const deadline = Date.now() + 120_000; +while (Date.now() < deadline) { + const receipt = await provider.getTransactionReceipt(hash); + if (receipt) return receipt; + await new Promise(r => setTimeout(r, 3000)); +} +throw new Error("Timed out waiting for confirmation"); +``` + +--- + +## 6. Skipping `submit-hash` After Broadcast + +**Error:** Broadcasting the transaction but not calling +`PUT /v1/transactions/{txId}/submit-hash`. + +**What happens:** The transaction succeeds on-chain, but Yield.xyz doesn't know about it. +Positions and balances appear stale. The user's dashboard shows no activity. + +**Fix:** After every successful broadcast, immediately call: +``` +PUT https://api.yield.xyz/v1/transactions/{txId}/submit-hash +{ "hash": "0x..." } +``` + +--- + +## 7. Modifying `unsignedTransaction` + +**Error:** Changing any field in the `unsignedTransaction` object before signing +(addresses, amounts, gas, data, etc.). + +**What happens:** Loss of funds. The transaction may send tokens to the wrong address, +approve unlimited spending, or interact with the wrong contract. + +**Fix:** Sign `unsignedTransaction` exactly as returned. If the amount or parameters are +wrong, create a new action — never edit an existing one. + +--- + +## 8. Hardcoding Validator Addresses + +**Error:** Using a hardcoded validator address for staking yields. + +**What happens:** The validator may be inactive, jailed, or decommissioned. The transaction +fails or delegates to a non-performing validator. + +**Fix:** Always fetch validators from `GET /v1/yields/{yieldId}/validators` and let the +user choose. Use the `preferred` flag to recommend curated validators. + +--- + +## 9. Not Checking the Yield Schema Before Actions + +**Error:** Calling `/v1/actions/enter` without first inspecting the yield's +`mechanics.arguments.enter` schema. + +**What happens:** Missing required fields (e.g., `validatorAddress` for staking, +`cosmosPubKey` for Cosmos, `tronResource` for Tron) cause 400 errors. + +**Fix:** Always call `GET /v1/yields/{yieldId}` first and read `mechanics.arguments.enter` +(or `.exit`). Each yield declares exactly what fields it requires — the schema is the contract. + +--- + +## 10. Using MCP Action Tools to Inspect API Responses + +**Error:** Calling MCP action tools (`yields_get_all`, `yields_get`, etc.) to see what +the API returns, then building code based on that response. + +**What happens:** The MCP returns trimmed/slimmed responses that omit fields present in +the full REST API response. Code built against MCP responses will be missing fields, +have wrong types, or break on edge cases. + +**Fix:** Always call the REST API directly with the user's API key to inspect actual +responses. Use `https://api.yield.xyz/docs.json` (or the `yield_get_api_spec` doc tool) +for the authoritative schema. + +--- + +## 11. Browser Wallet Error Handling + +**Error:** Assuming wallet errors are `Error` instances and using `catch (e) { e.message }`. + +**What happens:** MetaMask and Phantom throw plain objects `{ code, message }`, not +`Error` instances. `err.message` may be `undefined`, causing unhelpful error display. + +**Fix:** Always extract errors defensively: +```typescript +function extractWalletError(err: unknown): string { + if (err instanceof Error) return err.message; + if (typeof err === "object" && err !== null) { + const e = err as Record; + if (typeof e.message === "string") return e.message; + } + return String(err); +} +``` + +--- + +## 12. Not Handling Multi-Step Transactions + +**Error:** Assuming every action returns a single transaction. + +**What happens:** EVM actions often return multiple transactions (e.g., ERC-20 approve + +deposit). If only the first is executed, the deposit never happens. If executed out of +order, the deposit fails because the approval hasn't been confirmed yet. + +**Fix:** Always iterate `transactions[]` sorted by `stepIndex`. Wait for on-chain +confirmation of each transaction before starting the next. + +--- + +## 13. Assuming an HTTP Status Is From `api.yield.xyz` When It Isn't + +**Error:** Debugging an unexpected HTTP status (e.g. `412 Precondition Failed`, +`502`, `520`, `530`) as if it came from the Yield.xyz API, when the code isn't +documented for the endpoint you called. + +**What happens:** You waste time looking for a Yield.xyz-side cause that doesn't +exist. The status almost certainly came from something *between* your app and +our API — a CDN, reverse proxy, HTTP middleware, or service worker — not from +`api.yield.xyz`. + +Documented Yield.xyz error codes per endpoint: `400`, `401`, `403`, `404`, `422`, +`429`, `500`. Anything else is suspect. `412` in particular is never returned by +any Yield.xyz endpoint — it's commonly injected by edge layers for +`If-Match` / `If-Unmodified-Since` preconditions. + +**Fix:** Before treating an unusual status as an API bug: + +1. **Cross-check the live spec.** Call `yield_get_api_spec({ endpoint: "/v1/..." })` + or `yield_troubleshoot_error({ error, context })` — the troubleshoot tool + will confirm whether the code is documented for that endpoint. +2. **Inspect the raw response body.** If it's HTML or not our standard JSON + error shape `{ message, error, statusCode }`, the response is from an + intermediary, not us. +3. **Check your own stack:** HTTP client middleware, CDN (Cloudflare, Fastly), + reverse proxy (nginx, AWS ALB), or service worker rewriting responses. +4. **Double-check the actual status code.** `422` can be misread as `412` in + logs — verify from the network tab or a fresh `curl`. + +Only contact `hello@yield.xyz` once you've confirmed the response is from +`api.yield.xyz` directly with our JSON error shape. + +--- + +## 14. `@stakekit/widget` Requires React 19 (peer-dep is misleading) + +**Error:** Integrating `@stakekit/widget` with React 18, the app crashes on first +render with: + +``` +TypeError: Cannot read properties of undefined (reading 'H') + at Uk.c (chunk-...js) + at mgn (chunk-...js) +``` + +**What happens:** The widget's published bundle is compiled with the React Compiler +and calls `React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H.useMemoCache(...)`. +That internals shape only exists in React 19. The widget's `package.json` peer +declares `react: >=18`, but the runtime requirement is React **19+**. With React +18 installed, `H` is `undefined` and the very first hook call throws. + +**Fix:** When generating any project that uses `@stakekit/widget`, pin React 19+: + +```json +{ + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0" + } +} +``` + +If the user already has React 18 in the host project, surface this conflict +explicitly before running `pnpm install` — don't quietly downgrade or duplicate +React. A single React copy at v19 across the app is the only configuration that +works. + +If a fresh `pnpm install` leaves a stale `react@18` entry in the pnpm store, run +`pnpm why react` to confirm only `19.x` is actually linked, then clear Vite's +dep cache (`rm -rf node_modules/.vite`) before restarting the dev server. + +--- + +## 15. `@stakekit/widget` Pre-selecting a Specific Yield + +**Error:** Passing a `yieldId` prop to `` to land on a specific yield +(e.g. Solana SOL native staking, ETH Lido). The prop is ignored and the widget +defaults to ETH/Lido. + +**What happens:** `SKApp` does not accept a `yieldId` prop. The supported way to +preselect a yield is `preferredTokenYieldsPerNetwork`, keyed by +`SupportedSKChains` → `TokenString` → yieldId. `TokenString` is built as +`` `${token.network}-${token.address?.toLowerCase()}` ``. **For native tokens +(no contract address), the resulting string is `"-undefined"`** — the +literal word `undefined`, because `undefined?.toLowerCase()` is `undefined` and +template literals coerce it to the string `"undefined"`. + +**Fix:** For native staking yields (SOL, ATOM, DOT, ETH, etc.), use the +`-undefined` key: + +```tsx +import { MiscChainIds, SKApp } from '@stakekit/widget'; + + +``` + +`initialChain` alone is **not** enough — without `preferredTokenYieldsPerNetwork` +the widget defaults to ETH/Lido even when `initialChain` is set to a non-EVM +network. Always pass both. For ERC-20 / SPL tokens with a real contract address, +use `-` as the key instead. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md new file mode 100644 index 0000000..d533ee2 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md @@ -0,0 +1,139 @@ +# Integration Patterns by Product Type + +Architecture guidance for different types of Yield.xyz integrations. +Read the section that matches the user's product type. + +For all patterns: always fetch the live OpenAPI spec (`https://api.yield.xyz/docs.json`) +before generating code. Do not hardcode field names from this file. + +--- + +## Custody Platform + +**Approach:** Direct REST API with server-side signing + +Custody platforms manage keys on behalf of users and need full control over the transaction lifecycle. + +### Architecture +``` +User Dashboard -> Your Backend -> Yield.xyz API -> Your Signing Infrastructure -> Blockchain +``` + +### Key Considerations +- **Server-side signing:** You control the keys — use ethers.js, viem, or web3.js (see `signing-patterns.md`) +- **Multi-chain support:** Use `GET /v1/yields?network=X` to discover available yields per chain +- **Fee monetization:** Set up FeeWrapper contracts (EVM) or atomic fee transfers (non-EVM) via the Programmatic Access API +- **Shield validation:** Always validate before signing with `@yieldxyz/shield` +- **Audit trail:** Log every action, transaction hash, and submit-hash call + +### API Flow +1. `GET /v1/yields` — discover available yields for your supported chains +2. `GET /v1/yields/{yieldId}` — read mechanics and arguments schema +3. `POST /v1/actions/enter` — get unsigned transactions +4. Validate with Shield -> Sign with your HSM/KMS -> Broadcast +5. `PUT /v1/transactions/{txId}/submit-hash` — report hash +6. `POST /v1/yields/balances` — track positions + +--- + +## Consumer Wallet + +**Approach:** TypeScript SDK or Widget component + +Consumer wallets need a seamless UX with client-side signing (user controls keys). + +### Architecture +``` +Mobile/Web App -> @yieldxyz/sdk -> Yield.xyz API + | + User signs locally (browser wallet) +``` + +### Key Considerations +- **Client-side signing:** User signs with their own wallet — see `signing-patterns.md` for SDK recommendations per wallet +- **Widget option:** For fastest integration, use `@stakekit/widget` — a drop-in React component +- **Multi-wallet support:** Use wagmi + RainbowKit or Web3Modal for supporting multiple wallets with one integration +- **Chain detection:** Use the connected wallet's chain to filter relevant yields +- **User-friendly amounts:** The API uses human-readable amounts — display as-is +- **Browser wallet quirks:** See `common-pitfalls.md` entries #4, #5, #11 + +### Widget Integration (Fastest Path) +The `@stakekit/widget` is a pre-built React component that handles the entire yield +flow — discovery, entry, exit, and management. No need to call the API directly. + +Refer to the widget SDK docs for installation and configuration. + +--- + +## Neobank / Fintech + +**Approach:** REST API with Programmatic Access API for project management + +Neobanks need white-label yield with fee monetization and multi-tenant support. + +### Architecture +``` +Your App -> Your Backend -> Yield.xyz API (per-tenant API keys) + | + Programmatic Access API (project management) +``` + +### Key Considerations +- **Multi-tenant:** Use the Programmatic Access API to create per-customer or per-product API keys +- **Fee monetization:** Configure deposit fees (0.2-0.8%), performance fees (10-30%), and management fees (1-5%) via the Programmatic Access API +- **Allocator Vaults (OAVs):** Use for automated multi-strategy allocation with Smart Routing +- **Compliance:** Implement guardrails for allowed networks, risk levels, and transaction limits +- **Reporting:** Use `POST /v1/yields/balances` to generate portfolio reports +- **Common pitfalls:** See `common-pitfalls.md` — especially entries #1, #2, #6 + +--- + +## Yield Aggregator + +**Approach:** REST API with extensive yield discovery + +Aggregators need to compare yields across protocols and optimize allocation. + +### Architecture +``` +Your Frontend -> Your Backend -> Yield.xyz API + | + Compare all yields -> Auto-allocate +``` + +### Key Considerations +- **Yield discovery:** Use `GET /v1/yields` with filters to find all available opportunities. Pass `network`, `token`, `type`, `provider` as API query params — never fetch-all and filter client-side. +- **APY comparison:** Pass `sort` as an API query param (e.g. `?sort=apy:desc` — check the live spec via `yield_get_api_spec({ endpoint: "/v1/yields" })` for the exact sort syntax and supported fields). **Do NOT** fetch the paginated response and then `.sort()` on the client — that produces wrong results across pages and defeats pagination. Client-side sort is only acceptable if the live spec shows no `sort` param exists, which is rare. +- **Text search:** If users can type a protocol / token name, pass `search=` (or whatever the live spec calls it) through to the API rather than a client-side `.toLowerCase().includes(...)`. +- **Risk scoring:** Factor in protocol risk, TVL, and audit status from the yield response +- **Auto-rebalancing:** Periodically check APYs and move funds to higher-yielding opportunities +- **Cache aggressively:** Yield metadata doesn't change frequently — cache for 5-15 minutes + +--- + +## Enterprise Backend + +**Approach:** REST API with infrastructure-grade patterns + +Enterprise integrations need reliability, monitoring, and compliance controls. + +### Key Considerations +- **Idempotency:** The API is idempotent for read operations. For actions, use unique request IDs +- **Monitoring:** Track API latency, error rates, and transaction success rates +- **Disaster recovery:** Cache unsigned transactions and implement retry logic +- **Compliance:** Implement configurable guardrails for allowed networks and risk thresholds +- **Multi-region:** Use geographically distributed API calls for resilience +- **Timeout:** Set a 3-second max timeout on all API calls to avoid hanging requests + +--- + +## Mobile App + +**Approach:** REST API + WalletConnect or embedded wallet SDK + +### Key Considerations +- **WalletConnect:** Connect to external wallets (MetaMask Mobile, Rainbow, etc.) — see `signing-patterns.md` for the WalletConnect SDK +- **Embedded wallets:** Use Privy, Dynamic, or similar for in-app key management +- **Deep linking:** Handle wallet signature requests via deep links +- **Offline handling:** Cache yield data for browsing, require connectivity for actions +- **React Native:** Use `@yieldxyz/sdk` with a polyfill for `fetch` if needed diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md new file mode 100644 index 0000000..bb4d8bb --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md @@ -0,0 +1,136 @@ +# MCP Tool Reference — Builder Skill + +This file enumerates **every tool exposed by the `yield-agentkit` / `yield-xyz` MCP server** and tells the builder skill exactly which ones to use, which ones to avoid, and why. + +Consult this file whenever you're unsure if a given MCP tool is safe to invoke during a **builder session** (i.e. generating integration code, scaffolding an app, answering integration questions). When in doubt, default to the REST API with the user's own key rather than an MCP tool. + +--- + +## Rule Of Thumb + +| Category | Use during builder sessions? | Why | +|---|---|---| +| **Doc tools** (`yield_*` prefixed with `get_`, `lookup`, `fetch`, `list`, `troubleshoot`) | ✅ Yes — freely | Read-only references pulled from live docs / OpenAPI spec. Authoritative and always current. | +| **Action tools** (`yields_get*`, `actions_*`) | ❌ **No — never** | Return trimmed/slimmed responses that omit fields present in the full REST API. Code built from their responses will be wrong. Use `curl`/`fetch` against `https://api.yield.xyz` with the user's API key instead. | + +The **one purpose** of action tools is to let an end-user execute yield flows via an AI agent — not to let a builder inspect schemas. For building, always go through the live REST API. + +--- + +## ✅ Doc Tools — Use These + +All doc tools are read-only and safe. Prefer them over web searches or guessing. + +### `yield_get_overview` +Full tool index and routing guide for the Yield.xyz MCP. Covers action tools vs. doc tools, verification rules, and common flows. +**Use when:** "where do I start", "which tool", "overview", "how does this work", "getting started". + +### `yield_list_doc_topics` +Structured map of all Yield.xyz documentation, organized by category. Accepts optional `category` filter. +**Use when:** "what docs exist", "show me all topics", "what can I read about", "documentation overview". + +### `yield_lookup_docs` +Full-text search across 209 Yield.xyz documentation pages. Returns ranked results (title, excerpt, `.md` URL). Follow up with `yield_fetch_doc` to read the full page. +**Use when:** "how does X work", "find docs on balances", "Solana signing format", "what fields does actions/enter accept". + +### `yield_fetch_doc` +Fetches the full markdown content of any `docs.yield.xyz` page. Use after `yield_lookup_docs` returns a URL. Only accepts `docs.yield.xyz` URLs. +**Use when:** You have a doc URL and need the authoritative spec content with exact field names, types, and examples. + +### `yield_get_api_spec` +Fetches the **live** Yield.xyz OpenAPI spec from `https://api.yield.xyz/docs.json`. This is the **source of truth** for request bodies, field constraints (min/max/enum), error response shapes, and operationIds. Accepts optional `endpoint` filter (e.g. `/v1/actions/enter`) and `section` filter. +**Use when:** Before generating any integration code. Always verify current field names against this tool — don't trust memory. + +### `yield_get_api_limits` +Rate limits, API key tiers, throttling behavior, retry guidance. +**Use when:** "rate limits", "429", "how many requests", "production key", "throttling". + +### `yield_get_chain_guide` +How to handle Yield.xyz transactions on a specific blockchain. Covers `unsignedTransaction` format, encoding, parsing, which signing SDK to use, required chain-specific arguments (e.g. `cosmosPubKey`, `tezosPubKey`, `tronResource`), common gotchas, and example API flow. Resolves chain family live from `GET /v1/networks` — supports all 90+ networks. +**Use when:** "how do transactions work on Cosmos/Solana/Base", "what format is unsignedTransaction on [chain]", "what signing SDK for [chain]". + +### `yield_get_safety_rules` +Safety guardrails and pre-execution checks. Risk levels, 6 pre-execution checks, 7 safety rules, configurable guardrails. +**Use when:** "safety checks", "guardrails", "pre-action checklist", "risk controls". + +### `yield_get_tool_reference` +Complete reference for the 7 core Yield.xyz action tools (`yields_get_all`, `yields_get`, `yields_get_validators`, `yields_get_balances`, `actions_enter`, `actions_exit`, `actions_manage`). Explains inputs, outputs, example prompts, transaction format, and execution rules. +**Use when:** Explaining to the user what the MCP action tools do (e.g. "what tools are available", "how does actions_enter work"). **Do not use as justification to actually call those action tools in a builder session.** + +### `yield_get_transaction_guide` +Step-by-step guide through the entire transaction flow: discover → read schema → call action → handle unsigned transactions → sign → broadcast → submit hash → confirm. Includes server-side and browser wallet (MetaMask, Phantom EVM) signing examples. Accepts optional `chain` parameter for chain-specific examples. +**Use when:** "how do transactions work", "full flow", "end to end", "what's stepIndex", "submit hash", "how to sign", "transaction lifecycle", "MetaMask", "browser wallet", "waitForTransaction", "invalid params". + +### `yield_get_yields_endpoint_guide` +Complete reference for `GET /v1/yields` — the single most-used Yield.xyz endpoint. Covers every query param (filters: `network`/`networks`, `token`/`inputToken`/`inputTokens`, `provider`/`providers`, `type`/`types`, `yieldId`/`yieldIds` up to 100, `chainId`, `hasCooldownPeriod`, `hasWarmupPeriod`), **server-side `search`**, **server-side `sort`** (`YieldSortingOption` enum — `rewardRateDesc` = APY desc), pagination (`limit`/`offset`), response shape, and one-call query patterns that replace client-side fan-out. Also covers all 8 `YieldType` values (staking, liquid-staking, restaking, lending, vault, liquidity-pool, concentrated-liquidity-pool, real-world-asset) with mechanics, lock periods, required arguments, typical APY. Accepts optional `yieldType` filter for a single type's section. +**Use when:** "how do I filter yields", "how do I sort by APY", "can I query multiple chains at once", "what params does /v1/yields take", "server-side sort", "search yields", "yields_get_all options", "staking vs lending", "lock periods", "what yield types exist", "which type needs validators", "receipt tokens", "concentrated liquidity vs regular liquidity pool", "real-world asset yields". +**Backwards-compat:** the old tool name `yield_get_yield_types_guide` is kept as an alias — both names work, same content. + +### `yield_troubleshoot_error` +Diagnoses API errors, HTTP status codes, or unexpected responses. For unrecognized errors, automatically looks up the live OpenAPI spec for the authoritative error shape. +**Use when:** user says "I'm getting a 422", "400 error", "rate limited", "transaction failed", "FAILED status", "why did this fail", or pastes any error code / message from the API. + +--- + +## ❌ Action Tools — **DO NOT USE** During Builder Sessions + +These tools exist to let end-users execute yield flows via an agent. They are **not** a reliable reference for generating integration code because: + +1. **Responses are trimmed** — the MCP strips fields to keep context small. Full REST responses have more. +2. **Responses may be reshaped** — field names / nesting can differ from the raw API. +3. **Code built against MCP responses will be wrong** — users will hit missing fields in production. + +Instead, for **any** data lookup during a build session, use the user's API key against `https://api.yield.xyz` directly (`curl`, `fetch`, `axios`, etc.). + +| Tool | REST equivalent to call instead | +|---|---| +| `yields_get_all` | `GET https://api.yield.xyz/v1/yields?network=&token=&type=&provider=` | +| `yields_get` | `GET https://api.yield.xyz/v1/yields/{yieldId}` | +| `yields_get_validators` | `GET https://api.yield.xyz/v1/yields/{yieldId}/validators` | +| `yields_get_balances` | `POST https://api.yield.xyz/v1/yields/balances` | +| `yields_get_risk` | `GET https://api.yield.xyz/v1/yields/{yieldId}/risk` | +| `yields_get_tvl_history` | `GET https://api.yield.xyz/v1/yields/{yieldId}/tvl/history` | +| `yields_get_reward_rate_history` | `GET https://api.yield.xyz/v1/yields/{yieldId}/reward-rate/history` | +| `actions_enter` | `POST https://api.yield.xyz/v1/actions/enter` | +| `actions_exit` | `POST https://api.yield.xyz/v1/actions/exit` | +| `actions_manage` | `POST https://api.yield.xyz/v1/actions/manage` | +| `actions_get` | `GET https://api.yield.xyz/v1/actions/{actionId}` | +| `actions_get_all` | `GET https://api.yield.xyz/v1/actions` | +| `get_transaction` | `GET https://api.yield.xyz/v1/transactions/{transactionId}` | +| `submit_hash` | `PUT https://api.yield.xyz/v1/transactions/{transactionId}/submit-hash` | +| `networks_get_all` | `GET https://api.yield.xyz/v1/networks` | +| `providers_get_all` | `GET https://api.yield.xyz/v1/providers` | +| `yields_get_kyc_status` | `GET https://api.yield.xyz/v1/yields/{yieldId}/kyc/status` | + +**Exception — the `yields_get` tool for self-documenting schema.** Even here, prefer `yield_get_api_spec` + a live REST call. The REST response carries the full `mechanics.arguments` schema; the MCP response may not. + +--- + +## Decision Flow + +When you need information during a builder session: + +``` +Need API reference / schema? → yield_get_api_spec +Need sort/search/filter params for + /v1/yields specifically? → yield_get_yields_endpoint_guide — covers every + filter, multi-value syntax, sort enum, and + one-call patterns (replaces client-side fan-out) +Need other endpoint schemas? → yield_get_api_spec({ endpoint: "..." }) +Need transaction / signing guide? → yield_get_transaction_guide or yield_get_chain_guide +Need yield-type mechanics? → yield_get_yields_endpoint_guide (Part 2) +Need safety / limits / policies? → yield_get_safety_rules / yield_get_api_limits +Need to explore docs broadly? → yield_list_doc_topics → yield_lookup_docs → yield_fetch_doc +Hit an error? → yield_troubleshoot_error + +Need real yield / balance / action data? + → DO NOT use MCP action tools. + → Use the user's API key to call the REST endpoint at + https://api.yield.xyz directly and inspect the raw JSON. +``` + +--- + +## Why This Matters + +The builder skill's job is to produce **production-ready code**. That requires accurate schemas. MCP action tools are tuned for token efficiency in agent chats, not for API fidelity — so they are the wrong reference surface for code generation. Sticking to doc tools + live REST calls is what prevents the skill from hallucinating field names or generating calls that silently drop required data. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md new file mode 100644 index 0000000..ef821ec --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md @@ -0,0 +1,68 @@ +# Output Formats for Builder Skill + +Display and formatting rules for when generating UI code that shows yield data. + +**Important:** Always fetch the actual API response (using the user's API key) to discover +current field names before generating display code. Do not assume field names from this file. + +--- + +## Number Formatting + +When generating code that displays yield data, use these formats: + +| Data | Format | Example | +|---|---|---| +| APY/APR | Multiply by 100, show 2 decimals, add `%` | `7.02%` | +| TVL | Compact notation | `$4.93M`, `$322K`, `$1.2B` | +| Amounts | Token decimals, no trailing zeros | `1,000 USDC`, `0.5 ETH` | +| USD values | 2 decimal places | `$1,000.00` | +| Lockup/cooldown | Human-readable time from seconds | `7 days`, `21 days` | + +--- + +## Yield Listing + +When generating a yield discovery UI, display results in a table sorted by APY descending. + +Recommended columns: Protocol, Name/Vault, APY, TVL, Type, Network, Status. + +### TVL Filtering + +Filter out low-TVL yields before displaying: + +| Token Type | Minimum TVL | +|---|---| +| Stablecoins (USDC, USDT, DAI) | $500K | +| ETH / LSTs | $1M | +| BTC / wrapped BTC | $500K | +| Other tokens | $100K | + +--- + +## Transaction Summary + +After building an action, display a summary before the user signs: + +Show: yield name, network, amount, token, and the list of transactions to sign +(with their step index and type — e.g., "APPROVAL", "SUPPLY"). + +Remind the user to sign and broadcast in `stepIndex` order and wait for confirmation +between each. + +--- + +## Reward Rate Breakdown + +When showing a single yield's APY, expand the components array to show where the +yield comes from (base lending rate, incentive rewards, etc.). + +Flag any incentive/bonus component as potentially temporary. + +--- + +## Balance Display + +When generating a portfolio/balance UI, sort positions by USD value descending. +Highlight claimable rewards prominently. Show pending actions with clear call-to-action +buttons. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md new file mode 100644 index 0000000..21021d1 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md @@ -0,0 +1,44 @@ +# API Usage Policies + +Guidelines for efficient API usage in Yield.xyz integrations. + +--- + +## Rate Limits + +- Default rate limit depends on your API key tier (trial/standard/pro) +- If you receive `429 Too Many Requests`, respect the `retry-after` header +- Implement exponential backoff for retries + +--- + +## Caching Recommendations + +| Data Type | Cache Duration | Notes | +|---|---|---| +| Yield list (`/v1/yields`) | 5-15 minutes | APYs update periodically | +| Yield metadata (`/v1/yields/{id}`) | 5-15 minutes | Schema rarely changes | +| Validators | 15-30 minutes | Validator set changes slowly | +| Balances | Do not cache | Always fetch fresh | +| Networks | 1 hour | Rarely changes | + +--- + +## Pagination + +- Default `limit`: 20 +- Maximum `limit`: 50 +- Use `offset` for pagination +- Do not attempt to fetch all yields in a single request + +--- + +## Best Practices + +1. **Fetch only what you need.** Use query parameters (`network`, `token`, `type`) to filter server-side +2. **Cache yield metadata.** The schema and mechanics don't change between requests +3. **Don't poll balances excessively.** Check after user actions, not on a tight loop +4. **Use the SDK for TypeScript.** It handles pagination, typing, and error handling +5. **Log all submit-hash calls.** If a hash submission fails, positions won't update — you need to retry +6. **Handle 503 gracefully.** Upstream protocols can be temporarily unavailable +7. **Set a 3-second timeout** on all API calls to avoid hanging requests diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md new file mode 100644 index 0000000..dde584e --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md @@ -0,0 +1,100 @@ +# Setup Guide + +## Prerequisites + +- An MCP-compatible AI agent (Claude Code, Codex, Gemini CLI, etc.) +- A Yield.xyz API key (get one at https://dashboard.yield.xyz/login) +- Node.js 18+ (for SDK usage) or any HTTP client (for REST API) + +--- + +## Step 1 — Register the Yield.xyz MCP Server (REQUIRED — do this first) + +**This step is mandatory, not optional.** The builder skill depends on the doc tools +exposed by the `yield-agentkit` MCP server (`yield_get_api_spec`, +`yield_get_chain_guide`, `yield_get_transaction_guide`, `yield_troubleshoot_error`, +etc.). Without the MCP registered, the skill cannot fetch the live OpenAPI spec, +look up chain-specific signing guidance, or diagnose API errors — so code generation +will fall back on stale/hallucinated information. + +**The skill MUST auto-configure the MCP for the user as the very first action** when +the skill is invoked (before asking for an API key, before anything else). + +### Automatic configuration + +Run the correct command for the user's agent: + +```bash +# Claude Code +claude mcp add yield-agentkit --transport http https://mcp.yield.xyz/mcp + +# Codex, Gemini CLI, or any agent using an MCP config file — write to ~/.mcp.json +# or the project-local .mcp.json: +# { +# "mcpServers": { +# "yield-agentkit": { +# "command": "npx", +# "args": ["-y", "mcp-remote", "https://mcp.yield.xyz/mcp"] +# } +# } +# } +``` + +### Verify registration + +After registering, confirm the server is connected: + +```bash +claude mcp list +``` + +The output should include `yield-agentkit` with status `✓ Connected`. If it fails, +re-run the add command or inspect `.mcp.json` for typos. + +**Do not proceed to Step 2 until the MCP is registered and connected.** + +--- + +## Step 2 — Get a Yield.xyz API Key + +If you don't have one yet: +1. Go to https://dashboard.yield.xyz/login +2. Create a project +3. Copy your API key + +Set it as an environment variable: + +```bash +# .env +YIELD_API_KEY=your_api_key_here +``` + +--- + +## Step 3 — Verify API Access + +Test that your key works: + +```bash +curl -s "https://api.yield.xyz/v1/yields?network=base&token=USDC&limit=1" \ + -H "x-api-key: $YIELD_API_KEY" | jq . +``` + +If you get a JSON response with yield data, you're ready to build. + +--- + +## Step 4 — Install SDK (optional) + +For TypeScript/JavaScript projects, the SDK provides typed wrappers: + +```bash +npm install @yieldxyz/sdk +# or +pnpm add @yieldxyz/sdk +# or +yarn add @yieldxyz/sdk +``` + +For other languages, use the REST API directly — no SDK needed. +The full OpenAPI spec is at `https://api.yield.xyz/docs.json`. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md new file mode 100644 index 0000000..0efcef7 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -0,0 +1,156 @@ +# Signing Patterns & Wallet SDKs + +This file lists the recommended SDKs and libraries for signing Yield.xyz transactions, +organized by wallet and chain. Use the official SDK docs to generate integration code — +do not hardcode signing logic from snippets. + +--- + +## General Principles + +Regardless of wallet or chain: + +1. `unsignedTransaction` on EVM is a **JSON-encoded string** — always `JSON.parse()` first +2. `unsignedTransaction` on Solana is hex or base64 encoded bytes — decode before use +3. **Never modify any field** in `unsignedTransaction` — sign exactly as returned +4. Execute transactions in `stepIndex` order — wait for confirmation between each +5. After broadcasting, always call `PUT /v1/transactions/{txId}/submit-hash` + +--- + +## EVM — Server-Side Signing + +For backends, custody platforms, and any environment where you control the private key. + +### Recommended Libraries + +| Library | Use Case | Docs | +|---|---|---| +| **ethers.js v6** | General-purpose EVM signing | https://docs.ethers.org/v6/ | +| **viem** | Type-safe, lightweight alternative to ethers | https://viem.sh | +| **web3.js v4** | Legacy projects, broad ecosystem | https://docs.web3js.org | + +### Approach +- Create a `Wallet` (ethers) or `WalletClient` (viem) with the private key and RPC provider +- `JSON.parse()` the `unsignedTransaction` +- Pass the parsed object directly to `sendTransaction()` — no modifications +- `receipt.wait()` (ethers) or `waitForTransactionReceipt()` (viem) works correctly server-side + +--- + +## EVM — Browser Wallet Signing + +For apps where the user signs in the browser with an injected wallet. + +### Recommended SDKs + +| Wallet | SDK / Integration | Docs | +|---|---|---| +| **MetaMask** | MetaMask SDK (`@metamask/sdk`) | https://docs.metamask.io/wallet/connect/ | +| **Phantom (EVM)** | Phantom provider (`window.phantom.ethereum`) | https://docs.phantom.app/solana/integrating/extension | +| **WalletConnect** | WalletConnect Web3Modal (`@web3modal/ethers` or `@web3modal/wagmi`) | https://docs.walletconnect.com/web3modal/about | +| **Rainbow** | RainbowKit (`@rainbow-me/rainbowkit`) | https://www.rainbowkit.com/docs/introduction | +| **Coinbase Wallet** | Coinbase Wallet SDK (`@coinbase/wallet-sdk`) | https://docs.cloud.coinbase.com/wallet-sdk/docs | + +### Multi-Wallet Abstraction + +For supporting multiple wallets with a single integration: + +| Library | What It Does | Docs | +|---|---|---| +| **wagmi + viem** | React hooks for wallet connection, signing, and state | https://wagmi.sh | +| **Web3Modal** | Drop-in modal supporting 300+ wallets via WalletConnect | https://docs.walletconnect.com/web3modal/about | +| **RainbowKit** | Polished wallet connection UI for React apps | https://www.rainbowkit.com/docs/introduction | +| **ConnectKit** | Wallet connection modal by Family | https://docs.family.co/connectkit | + +### Browser Wallet Signing Rules + +When signing with any browser wallet, these adjustments are required: + +1. **`JSON.parse()` the `unsignedTransaction`** — it's a JSON string on EVM +2. **Strip `nonce`, `type`, and `chainId`** — the wallet manages these. Keeping the + API-returned values causes stale simulation and triggers "likely to fail" warnings +3. **On L2 chains (Base, Arbitrum, Optimism, Polygon, etc.):** omit gas fields entirely + (`gasLimit`, `maxFeePerGas`, `maxPriorityFeePerGas`). Let the wallet estimate gas — + L2 gas changes rapidly and API values go stale +4. **On Ethereum mainnet:** you can optionally pass gas fields for a better fee estimate UX +5. **Use manual receipt polling** — `waitForTransaction()` hangs with injected providers +6. **Extract errors defensively** — browser wallets throw plain `{code, message}` objects, + not `Error` instances + +See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each issue. + +--- + +## Solana Signing + +### Recommended SDKs + +| Library | Use Case | Docs | +|---|---|---| +| **@solana/web3.js v2** | General-purpose Solana (new version) | https://solana.com/docs/clients/javascript | +| **@solana/web3.js v1** | Legacy projects | https://solana-labs.github.io/solana-web3.js/ | +| **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.app/solana/integrating/extension | +| **Backpack** | Browser wallet (multi-chain, popular in Solana) | https://docs.backpack.app | +| **Solana Wallet Adapter** | Multi-wallet abstraction for React | https://github.com/anza-xyz/wallet-adapter | + +### Approach +- Decode `unsignedTransaction` from hex or base64 to bytes +- Deserialize into a `Transaction` or `VersionedTransaction` +- Sign with keypair (server) or `signAndSendTransaction` (browser wallet) +- Use `confirmTransaction()` to wait for finalization + +--- + +## Cosmos Signing + +### Recommended SDKs + +| Library | Use Case | Docs | +|---|---|---| +| **@cosmjs/stargate** | General Cosmos signing and broadcasting | https://cosmos.github.io/cosmjs/ | +| **@cosmjs/proto-signing** | Protobuf-based signing | https://cosmos.github.io/cosmjs/ | +| **Keplr** | Browser wallet (most popular Cosmos wallet) | https://docs.keplr.app/api/ | +| **Leap** | Browser wallet (multi-chain Cosmos) | https://docs.leapwallet.io | +| **CosmosKit** | Multi-wallet abstraction for React | https://cosmoskit.com | + +### Cosmos-Specific Requirement +Cosmos yields require the user's **public key** as an additional argument when calling the +action endpoint. Include `additionalAddresses.cosmosPubKey` in the request body. +Fetch the current schema from the API spec to confirm the exact field name. + +--- + +## Tron Signing + +### Recommended SDKs + +| Library | Use Case | Docs | +|---|---|---| +| **TronWeb** | General-purpose Tron signing | https://tronweb.network/docu/docs/intro | +| **TronLink** | Browser wallet (most popular Tron wallet) | https://docs.tronlink.org | + +### Tron-Specific Requirement +Tron staking requires specifying the resource type (`"BANDWIDTH"` or `"ENERGY"`) in the +action arguments. Check the yield schema to confirm. + +--- + +## Transaction Lifecycle (All Chains) + +Regardless of chain or wallet, every transaction follows this lifecycle: + +``` +1. POST /v1/actions/enter (or /exit, /manage) + -> Returns transactions[] with unsignedTransaction + +2. For each transaction in stepIndex order: + a. Parse/decode unsignedTransaction (chain-specific) + b. Sign with user's key (server or browser wallet) + c. Broadcast to the network + d. Wait for on-chain confirmation + e. PUT /v1/transactions/{txId}/submit-hash <- MANDATORY + +3. After all transactions confirmed: + -> Position is active, balances update +``` From ba702fda90f18c2f033f532e6f4ba54a59d73879 Mon Sep 17 00:00:00 2001 From: Ishita Agarwal Date: Wed, 17 Jun 2026 00:07:08 +0530 Subject: [PATCH 02/23] feat: add perps and borrow api specs --- .../skills/yield-agentkit-builder/README.md | 13 ++- .../skills/yield-agentkit-builder/SKILL.md | 95 +++++++++++++++---- .../references/mcp-tools.md | 22 ++++- 3 files changed, 105 insertions(+), 25 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md index 7698421..6366218 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md @@ -3,9 +3,9 @@ [![AI Agent Skill](https://img.shields.io/badge/AI%20Agent-Skill-orange)](https://yield.xyz) [![Version](https://img.shields.io/badge/version-1.0.0-blue)](https://github.com/stakekit/agentkit) -> **Build DeFi yield apps with AI agents.** This skill teaches any MCP-compatible AI agent how to generate production-ready code that integrates with the Yield.xyz API — staking, lending, vaults, and yield across 80+ networks. +> **Build on Yield.xyz with AI agents.** This skill teaches any MCP-compatible AI agent how to generate production-ready code that integrates with the Yield.xyz APIs across all three products: **Yield** (staking, lending, vaults, RWA across 80+ networks), **Perps** (perpetual futures on Hyperliquid), and **Borrow** (lending/borrowing markets). -The skill works alongside the [Yield.xyz AgentKit MCP server](https://mcp.yield.xyz/mcp), which provides live API access for looking up yield schemas and field definitions during code generation. +The skill works alongside the [Yield.xyz AgentKit MCP server](https://mcp.yield.xyz/mcp), which provides live access to each product's OpenAPI spec and to public reference repos for looking up schemas, field definitions, and working code during generation. --- @@ -17,9 +17,16 @@ Once installed, the agent activates this skill when you ask to build something: - "Integrate USDC lending into my app" - "Generate a backend service that enters yield positions" - "Set up a neobank with DeFi yield features" +- "Integrate the Perps API for perpetual futures trading" +- "Add the perps widget to my site" +- "Wire the Borrow API into my backend" - "How do I sign Yield.xyz transactions with MetaMask?" -The agent will generate code that calls the Yield.xyz REST API directly, using correct field names, proper transaction signing, and the full submit-hash lifecycle. +It starts by asking which product you're building on — **Yield**, **Perps**, or +**Borrow** — then recommends the best integration option (widget, SDK, or direct +REST API) for what you're building. From there it generates code that calls the +right product's REST API directly, using correct field names, proper transaction +signing, and the full submit-hash lifecycle. --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 3b4f888..328ca0c 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -1,6 +1,6 @@ --- name: yield-agentkit-builder -description: Build applications that integrate with the Yield.xyz API. Generates production-ready code for staking, lending, vaults, and DeFi yield across 80+ networks. Covers REST API integration, transaction signing, wallet connection, and fee monetization. Use when user wants to build an app, integrate yield, generate code, or set up a project using Yield.xyz. +description: Build applications that integrate with the Yield.xyz APIs across all three products — Yield (staking, lending, vaults, RWA across 80+ networks), Perps (perpetual futures on Hyperliquid), and Borrow (lending/borrowing markets). Generates production-ready code covering REST API integration, transaction signing, wallet connection, and fee monetization. Use when user wants to build an app, integrate yield/perps/borrow, generate code, or set up a project using Yield.xyz. metadata: author: Yield.xyz version: "1.0.0" @@ -36,10 +36,17 @@ the `yield-agentkit` skill — not this one. ### 1. API Base URL -**`https://api.yield.xyz`** — this is the only correct production URL. +Each product has its own production base URL: -Do not use `api.stakek.it`, `api.stakekit.io`, or any other legacy domain. -Every code sample, fetch call, and SDK config must use `api.yield.xyz`. +| Product | Base URL | Live spec | +|---|---|---| +| **Yield** | `https://api.yield.xyz` | `https://api.yield.xyz/docs.json` | +| **Perps** | `https://perps.yield.xyz` | `https://perps.yield.xyz/docs.json` | +| **Borrow** | `https://borrow.yield.xyz` | `https://borrow.yield.xyz/docs.json` | + +These are the only correct production URLs. Do not use `api.stakek.it`, +`api.stakekit.io`, or any other legacy domain. Every code sample, fetch call, +and SDK config must use the base URL for the product being integrated. ### 2. API Key Requirement @@ -78,8 +85,9 @@ and up-to-date. Use it to look up endpoint schemas, field names, and constraints The API evolves continuously. Do not rely on hardcoded field names or schemas from this skill's reference files. Instead: -1. **Fetch the live OpenAPI spec** from `https://api.yield.xyz/docs.json` (or use the - `yield_get_api_spec` tool) to discover current field names, types, and constraints +1. **Fetch the live OpenAPI spec** for the product being integrated — `yield_get_api_spec({ product })`, + or the product's `docs.json` directly (`api.yield.xyz`, `perps.yield.xyz`, or + `borrow.yield.xyz`) — to discover current field names, types, and constraints 2. **Call the actual API endpoint** with the user's key to see the real response shape 3. **Then generate code** based on what the live spec and actual responses show @@ -230,10 +238,48 @@ If registration fails, stop and surface the error to the user — do not attempt build anything until the MCP is connected. Full details and config snippets are in [`references/setup.md`](./references/setup.md). -### Step 1 — Understand the Use Case +### Step 1 — Ask Which Product (do this BEFORE anything else) + +**The first thing you ask the user is which Yield.xyz product they want to integrate.** +This single question removes most of the ambiguity from the rest of the session — it +determines the API base URL, which spec to fetch, and which integration options to +recommend. Don't assume "Yield" just because that's the default; ask. + +Yield.xyz has **three products**: + +| Product | What it gives the integrator | Integration options to offer | +|---|---|---| +| **Yield** | DeFi staking, lending, vaults, and RWA yields across 80+ networks | Widget (drop-in React component), TypeScript/JS SDK, or direct REST API integration | +| **Perps** | Perpetual futures trading (Hyperliquid integrated) | Perps REST API (integrate directly), or the **perps widget** (public reference repo — get the link via `yield_list_repos`) | +| **Borrow** | Lending/borrowing markets | Borrow REST API (integrate the endpoints into your existing backend/app) | + +Ask plainly, e.g.: -Ask the user what they're building. The answer determines architecture, signing approach, -and which reference files to load. +> "Yield.xyz has three products you can build on: **Yield** (staking, lending, +> vaults, RWA yields), **Perps** (perpetual futures via Hyperliquid), and **Borrow** +> (lending/borrowing markets). Which one do you want to integrate?" + +Once they choose, recommend the integration option that best fits what they're +building (next step), and from then on use that product's base URL and spec +(`yield_get_api_spec({ product: "yield" | "perps" | "borrow" })`). + +### Step 2 — Understand the Use Case & Recommend an Approach + +Now ask what they're building. Combined with the product they chose, the answer +determines the integration option, architecture, signing approach, and which +reference files to load. + +- **Yield** — recommend Widget for the fastest drop-in path, the SDK for + TypeScript/JS apps that want typed API access, or direct REST for everything + else (other languages, custom backends). Then map their product type to the + signing/architecture pattern below. +- **Perps** — recommend the perps widget for a fast self-custodial trading UI + (fetch the repo link with `yield_list_repos` and read its source), or the Perps + REST API for a custom integration. +- **Borrow** — recommend integrating the Borrow REST API endpoints into their + existing app/backend. + +For **Yield**, map the product type to architecture and signing: | Product Type | Signing | Key Reference | |---|---|---| @@ -247,24 +293,33 @@ and which reference files to load. See **[`references/integration-patterns.md`](./references/integration-patterns.md)** for architecture diagrams and patterns per product type. -### Step 2 — Look Up the API Spec +When the docs don't fully resolve an integration question — or you're stuck +implementing one — use **`yield_list_repos`** to find the relevant public repo +(widget, perps widget, SDK, api-recipes, signers, shield, etc.) and read its raw +source as a working reference. + +### Step 3 — Look Up the API Spec -Before generating any code, fetch the live OpenAPI spec to discover current field names -and request/response shapes: +Before generating any code, fetch the live OpenAPI spec for the chosen product to +discover current field names and request/response shapes: -**Option A — Use the doc tool:** +**Option A — Use the doc tool (pass the product):** ``` -yield_get_api_spec({ endpoint: "/v1/actions/enter", section: "endpoints" }) +yield_get_api_spec({ product: "yield", endpoint: "/v1/actions/enter", section: "endpoints" }) +yield_get_api_spec({ product: "perps", query: "order" }) +yield_get_api_spec({ product: "borrow", query: "market" }) ``` -**Option B — Fetch directly with the user's key:** +**Option B — Fetch directly with the user's key (use the product's spec URL):** ```bash -curl https://api.yield.xyz/docs.json | jq '.paths["/v1/actions/enter"]' +curl https://api.yield.xyz/docs.json | jq '.paths["/v1/actions/enter"]' # Yield +curl https://perps.yield.xyz/docs.json | jq '.paths' # Perps +curl https://borrow.yield.xyz/docs.json| jq '.paths' # Borrow ``` Use the spec as the source of truth. Never assume field names from memory. -### Step 3 — Call the Real API to See Actual Responses +### Step 4 — Call the Real API to See Actual Responses Use the user's API key to make a real API call and inspect the response. This lets you see the actual field names, nesting, and data types in the response: @@ -276,10 +331,10 @@ curl -s "https://api.yield.xyz/v1/yields?network=base&token=USDC&limit=1" \ Use this real response as the reference when building the frontend or backend integration. -### Step 4 — Generate Code +### Step 5 — Generate Code Generate code that: -1. Uses `https://api.yield.xyz` as the base URL +1. Uses the chosen product's base URL (`api.yield.xyz`, `perps.yield.xyz`, or `borrow.yield.xyz`) 2. Passes the user's API key via `x-api-key` header 3. Uses field names exactly as seen in the live spec and API responses 4. Handles the full transaction lifecycle (action -> sign -> broadcast -> submit-hash) @@ -288,7 +343,7 @@ Generate code that: See **[`references/signing-patterns.md`](./references/signing-patterns.md)** for wallet SDK references and signing guidance per chain. -### Step 5 — Run the Project Yourself and Report URLs +### Step 6 — Run the Project Yourself and Report URLs **Do not tell the user "now run `npm run dev`" and walk away.** The skill's job isn't done until the project is actually running and you've handed the user the diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md index bb4d8bb..cf5e318 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md @@ -38,8 +38,17 @@ Fetches the full markdown content of any `docs.yield.xyz` page. Use after `yield **Use when:** You have a doc URL and need the authoritative spec content with exact field names, types, and examples. ### `yield_get_api_spec` -Fetches the **live** Yield.xyz OpenAPI spec from `https://api.yield.xyz/docs.json`. This is the **source of truth** for request bodies, field constraints (min/max/enum), error response shapes, and operationIds. Accepts optional `endpoint` filter (e.g. `/v1/actions/enter`) and `section` filter. -**Use when:** Before generating any integration code. Always verify current field names against this tool — don't trust memory. +Fetches the **live** OpenAPI spec for whichever Yield.xyz product you're integrating. This is the **source of truth** for request bodies, field constraints (min/max/enum), error response shapes, and operationIds. Accepts a `product` arg, an optional `endpoint` filter (e.g. `/v1/actions/enter`, `/v1/markets/{marketId}`), an optional `query` keyword search across paths, and `method`/`section` filters. + +The `product` arg selects which spec to fetch: + +| `product` | Spec URL | Covers | +|---|---|---| +| `yield` (default) | `https://api.yield.xyz/docs.json` | Staking, liquid/restaking, lending, vaults, RWA | +| `perps` | `https://perps.yield.xyz/docs.json` | Perpetual futures (Hyperliquid) | +| `borrow` | `https://borrow.yield.xyz/docs.json` | Lending/borrowing markets | + +**Use when:** Before generating any integration code, for any of the three products. Always verify current field names against this tool — don't trust memory. ### `yield_get_api_limits` Rate limits, API key tiers, throttling behavior, retry guidance. @@ -70,6 +79,10 @@ Complete reference for `GET /v1/yields` — the single most-used Yield.xyz endpo Diagnoses API errors, HTTP status codes, or unexpected responses. For unrecognized errors, automatically looks up the live OpenAPI spec for the authoritative error shape. **Use when:** user says "I'm getting a 422", "400 error", "rate limited", "transaction failed", "FAILED status", "why did this fail", or pastes any error code / message from the API. +### `yield_list_repos` +Lists the public StakeKit/Yield.xyz GitHub repos that serve as **working code references** for integrations — the staking/yield widget (and its Next.js / Vite reference apps), the **perps widget** (`perps-widget`), the TypeScript/JS SDKs, runnable `api-recipes`, the `signers` and `shield` transaction-security libraries, and an `assets` repo for token/provider logos. Returns each repo with a one-line description and the raw-file fetch pattern (`https://raw.githubusercontent.com/stakekit///`). +**Use when:** the other doc tools don't fully resolve an integration problem and you need to read real, working source code — e.g. "how does the widget wire wallet connect", "show me a runnable enter-position example", "how do I integrate the perps widget", "how does Shield validate a transaction". It points at code; it never returns live data — use the action tools / REST API for that. + --- ## ❌ Action Tools — **DO NOT USE** During Builder Sessions @@ -112,6 +125,11 @@ When you need information during a builder session: ``` Need API reference / schema? → yield_get_api_spec + (pass product: "perps" or "borrow" + when integrating those products; + default product is "yield") +Need a working code example / + stuck implementing something? → yield_list_repos, then read the raw source Need sort/search/filter params for /v1/yields specifically? → yield_get_yields_endpoint_guide — covers every filter, multi-value syntax, sort enum, and From 7e11a63b5085cd3020841a17c8fcf2ddee6aa778 Mon Sep 17 00:00:00 2001 From: Ishita Agarwal Date: Wed, 17 Jun 2026 09:39:28 +0530 Subject: [PATCH 03/23] feat: update mcp tools --- .../skills/yield-agentkit-builder/README.md | 3 +- .../skills/yield-agentkit-builder/SKILL.md | 60 ++++--- .../references/mcp-tools.md | 157 +++++++++++------- 3 files changed, 143 insertions(+), 77 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md index 6366218..87b2455 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md @@ -37,6 +37,7 @@ signing, and the full submit-hash lifecycle. | `yield-agentkit` | Explore yields conversationally | Tables, summaries, portfolio views | | `yield-agentkit-moonpay` | Enter yields end-to-end via MoonPay | Signed & broadcast transactions | | `yield-agentkit-privy` | Enter yields end-to-end via Privy | Signed & broadcast transactions | +| `yield-agentkit-rwakit-privy` | Enter RWA yields end-to-end via Privy | Signed & broadcast transactions | | **`yield-agentkit-builder`** | **Build apps that integrate Yield.xyz** | **Production-ready code** | The explore/execute skills use MCP tools directly. The builder skill uses MCP tools for research but generates code that calls the REST API with the user's own API key. @@ -114,7 +115,7 @@ yield-agentkit-builder/ | Reference | What's in it | |---|---| -| `common-pitfalls.md` | 12 real errors from builder sessions — wrong URLs, field names, gas issues, etc. | +| `common-pitfalls.md` | 12 real errors/common pitfalls — wrong URLs, field names, gas issues, etc. | | `signing-patterns.md` | Recommended SDKs for MetaMask, Phantom, WalletConnect, Rainbow, Coinbase, Solana, Cosmos | | `integration-patterns.md` | Architecture for custody, wallet, neobank, aggregator, enterprise, mobile | diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 328ca0c..901369f 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -60,25 +60,47 @@ Once you have the key: - Never hardcode the demo key into the user's codebase - Recommend storing it in an environment variable (e.g., `YIELD_API_KEY`) -### 3. Never Use MCP Action Tools — Always Use the REST API Directly - -**Do NOT call MCP action tools** (`yields_get_all`, `yields_get`, `yields_get_validators`, -`yields_get_balances`, `actions_enter`, `actions_exit`, `actions_manage`) to look up data -during a build session. The MCP returns trimmed/slimmed responses that omit fields present -in the full API. If you build code based on MCP responses, it will be wrong. - -**Instead:** Use the user's API key to call the REST API directly -(`https://api.yield.xyz/v1/...`) via `curl`, `fetch`, or any HTTP client. This gives you -the full, unmodified response — use that as the reference for building the integration. - -**The one exception:** The `yield_get_api_spec` doc tool is safe to use. It fetches -the live OpenAPI spec from `https://api.yield.xyz/docs.json`, which is always complete -and up-to-date. Use it to look up endpoint schemas, field names, and constraints. - -> 📖 **For the full list of MCP tools — which are safe to use in builder sessions -> (doc tools) and which must NOT be called (action tools) — see -> [`references/mcp-tools.md`](./references/mcp-tools.md).** Consult that file any -> time you're unsure whether a given MCP tool is appropriate for a build task. +### 3. Never Call MCP Action Tools in a Builder Session — and Never Advise the User to + +The MCP server exposes **17 action tools** (e.g. `yields_get_all`, `yields_get`, +`yields_get_balances`, `yields_get_kyc_status`, `actions_enter`, `actions_exit`, +`actions_manage`, `submit_hash` — the full list with descriptions is in +[`references/mcp-tools.md`](./references/mcp-tools.md)). + +**In a builder session you must never call any of them, and never advise the user +to call them as part of their integration.** They return trimmed/reshaped responses +that omit fields present in the full API, so code built against them will be wrong — +and execution/signing belongs to the dedicated skills below, not to a code-generation +session. + +**For building, get data from the REST API instead.** Use the user's API key to call +the live REST endpoint directly (`https://api.yield.xyz/v1/...`, or the perps/borrow +base) via `curl`, `fetch`, or any HTTP client — that gives the full, unmodified +response to build against. The only MCP tools you use are the read-only **doc tools** +(`yield_get_api_spec`, `yield_list_repos`, etc.). + +**Two things you _may_ do with action tools:** + +1. **Explain what an action tool does, if the user asks.** Describing a tool is fine — + the one-line descriptions are in [`references/mcp-tools.md`](./references/mcp-tools.md). + Explaining ≠ calling. +2. **If the user actually wants to run or test action tools** (enter a position, check + live balances, manage rewards), that is out of scope for this skill. Point them to + the dedicated skill that fits — each carries the wallet, signing, and security + guidance that action-tool execution requires: + + | If the user wants to… | Use this skill | What it provides | + |---|---|---| + | Explore yields / see what tools & yields exist, no wallet needed | **`yield-agentkit`** | Conversational discovery + action-tool reference; no signing | + | Execute (enter/exit/manage) with an privy agentic wallet | **`yield-agentkit-privy`** | Privy wallet — policy, signing, broadcast (requires Privy) | + | Execute with a MoonPay wallet | **`yield-agentkit-moonpay`** | MoonPay wallet auth + signing (requires MoonPay + MCP) | + | Execute tokenized RWA yields (KYC/accreditation gated) | **`yield-agentkit-rwakit-privy`** | RWA access gating on top of Privy execution | + + Don't reproduce execution/signing steps here — they live in those skills by design. + +> 📖 **For the full MCP tool list — which are safe in builder sessions (doc tools) +> and which must never be called (action tools), plus a one-line description and REST +> equivalent for each — see [`references/mcp-tools.md`](./references/mcp-tools.md).** ### 4. Never Hardcode Field Names — Always Fetch from the Live Spec diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md index cf5e318..70eb4ed 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md @@ -22,20 +22,24 @@ The **one purpose** of action tools is to let an end-user execute yield flows vi All doc tools are read-only and safe. Prefer them over web searches or guessing. ### `yield_get_overview` -Full tool index and routing guide for the Yield.xyz MCP. Covers action tools vs. doc tools, verification rules, and common flows. -**Use when:** "where do I start", "which tool", "overview", "how does this work", "getting started". +- Full tool index and routing guide for the Yield.xyz MCP. Covers action tools vs. doc tools, verification rules, and common flows. + +- **Use when:** "where do I start", "which tool", "overview", "how does this work", "getting started". ### `yield_list_doc_topics` -Structured map of all Yield.xyz documentation, organized by category. Accepts optional `category` filter. -**Use when:** "what docs exist", "show me all topics", "what can I read about", "documentation overview". +- Structured map of all Yield.xyz documentation, organized by category. Accepts optional `category` filter. + +- **Use when:** "what docs exist", "show me all topics", "what can I read about", "documentation overview". ### `yield_lookup_docs` -Full-text search across 209 Yield.xyz documentation pages. Returns ranked results (title, excerpt, `.md` URL). Follow up with `yield_fetch_doc` to read the full page. -**Use when:** "how does X work", "find docs on balances", "Solana signing format", "what fields does actions/enter accept". +- Full-text search across 209 Yield.xyz documentation pages. Returns ranked results (title, excerpt, `.md` URL). Follow up with `yield_fetch_doc` to read the full page. + +- **Use when:** "how does X work", "find docs on balances", "Solana signing format", "what fields does actions/enter accept". ### `yield_fetch_doc` -Fetches the full markdown content of any `docs.yield.xyz` page. Use after `yield_lookup_docs` returns a URL. Only accepts `docs.yield.xyz` URLs. -**Use when:** You have a doc URL and need the authoritative spec content with exact field names, types, and examples. +- Fetches the full markdown content of any `docs.yield.xyz` page. Use after `yield_lookup_docs` returns a URL. Only accepts `docs.yield.xyz` URLs. + +- **Use when:** You have a doc URL and need the authoritative spec content with exact field names, types, and examples. ### `yield_get_api_spec` Fetches the **live** OpenAPI spec for whichever Yield.xyz product you're integrating. This is the **source of truth** for request bodies, field constraints (min/max/enum), error response shapes, and operationIds. Accepts a `product` arg, an optional `endpoint` filter (e.g. `/v1/actions/enter`, `/v1/markets/{marketId}`), an optional `query` keyword search across paths, and `method`/`section` filters. @@ -51,71 +55,106 @@ The `product` arg selects which spec to fetch: **Use when:** Before generating any integration code, for any of the three products. Always verify current field names against this tool — don't trust memory. ### `yield_get_api_limits` -Rate limits, API key tiers, throttling behavior, retry guidance. -**Use when:** "rate limits", "429", "how many requests", "production key", "throttling". +- Rate limits, API key tiers, throttling behavior, retry guidance. + +- **Use when:** "rate limits", "429", "how many requests", "production key", "throttling". ### `yield_get_chain_guide` -How to handle Yield.xyz transactions on a specific blockchain. Covers `unsignedTransaction` format, encoding, parsing, which signing SDK to use, required chain-specific arguments (e.g. `cosmosPubKey`, `tezosPubKey`, `tronResource`), common gotchas, and example API flow. Resolves chain family live from `GET /v1/networks` — supports all 90+ networks. -**Use when:** "how do transactions work on Cosmos/Solana/Base", "what format is unsignedTransaction on [chain]", "what signing SDK for [chain]". +- How to handle Yield.xyz transactions on a specific blockchain. Covers `unsignedTransaction` format, encoding, parsing, which signing SDK to use, required chain-specific arguments (e.g. `cosmosPubKey`, `tezosPubKey`, `tronResource`), common gotchas, and example API flow. Resolves chain family live from `GET /v1/networks` — supports all 90+ networks. + +- **Use when:** "how do transactions work on Cosmos/Solana/Base", "what format is unsignedTransaction on [chain]", "what signing SDK for [chain]". ### `yield_get_safety_rules` -Safety guardrails and pre-execution checks. Risk levels, 6 pre-execution checks, 7 safety rules, configurable guardrails. -**Use when:** "safety checks", "guardrails", "pre-action checklist", "risk controls". +- Safety guardrails and pre-execution checks. Risk levels, 6 pre-execution checks, 7 safety rules, configurable guardrails. + +- **Use when:** "safety checks", "guardrails", "pre-action checklist", "risk controls". ### `yield_get_tool_reference` -Complete reference for the 7 core Yield.xyz action tools (`yields_get_all`, `yields_get`, `yields_get_validators`, `yields_get_balances`, `actions_enter`, `actions_exit`, `actions_manage`). Explains inputs, outputs, example prompts, transaction format, and execution rules. -**Use when:** Explaining to the user what the MCP action tools do (e.g. "what tools are available", "how does actions_enter work"). **Do not use as justification to actually call those action tools in a builder session.** +- Reference for the Yield.xyz action tools — inputs, outputs, example prompts, transaction format, and execution rules. (The MCP exposes **17 action tools** in total; the full list with one-line descriptions and REST equivalents is in the [Action Tools](#-action-tools--do-not-call-during-builder-sessions) section below.) + +- **Use when:** Explaining to the user what the MCP action tools do (e.g. "what tools are available", "how does actions_enter work"). **Do not use as justification to actually call those action tools in a builder session.** ### `yield_get_transaction_guide` -Step-by-step guide through the entire transaction flow: discover → read schema → call action → handle unsigned transactions → sign → broadcast → submit hash → confirm. Includes server-side and browser wallet (MetaMask, Phantom EVM) signing examples. Accepts optional `chain` parameter for chain-specific examples. -**Use when:** "how do transactions work", "full flow", "end to end", "what's stepIndex", "submit hash", "how to sign", "transaction lifecycle", "MetaMask", "browser wallet", "waitForTransaction", "invalid params". +- Step-by-step guide through the entire transaction flow: discover → read schema → call action → handle unsigned transactions → sign → broadcast → submit hash → confirm. Includes server-side and browser wallet (MetaMask, Phantom EVM) signing examples. Accepts optional `chain` parameter for chain-specific examples. + +- **Use when:** "how do transactions work", "full flow", "end to end", "what's stepIndex", "submit hash", "how to sign", "transaction lifecycle", "MetaMask", "browser wallet", "waitForTransaction", "invalid params". ### `yield_get_yields_endpoint_guide` -Complete reference for `GET /v1/yields` — the single most-used Yield.xyz endpoint. Covers every query param (filters: `network`/`networks`, `token`/`inputToken`/`inputTokens`, `provider`/`providers`, `type`/`types`, `yieldId`/`yieldIds` up to 100, `chainId`, `hasCooldownPeriod`, `hasWarmupPeriod`), **server-side `search`**, **server-side `sort`** (`YieldSortingOption` enum — `rewardRateDesc` = APY desc), pagination (`limit`/`offset`), response shape, and one-call query patterns that replace client-side fan-out. Also covers all 8 `YieldType` values (staking, liquid-staking, restaking, lending, vault, liquidity-pool, concentrated-liquidity-pool, real-world-asset) with mechanics, lock periods, required arguments, typical APY. Accepts optional `yieldType` filter for a single type's section. -**Use when:** "how do I filter yields", "how do I sort by APY", "can I query multiple chains at once", "what params does /v1/yields take", "server-side sort", "search yields", "yields_get_all options", "staking vs lending", "lock periods", "what yield types exist", "which type needs validators", "receipt tokens", "concentrated liquidity vs regular liquidity pool", "real-world asset yields". -**Backwards-compat:** the old tool name `yield_get_yield_types_guide` is kept as an alias — both names work, same content. +- Complete reference for `GET /v1/yields` — the single most-used Yield.xyz endpoint. Covers every query param (filters: `network`/`networks`, `token`/`inputToken`/`inputTokens`, `provider`/`providers`, `type`/`types`, `yieldId`/`yieldIds` up to 100, `chainId`, `hasCooldownPeriod`, `hasWarmupPeriod`), **server-side `search`**, **server-side `sort`** (`YieldSortingOption` enum — `rewardRateDesc` = APY desc), pagination (`limit`/`offset`), response shape, and one-call query patterns that replace client-side fan-out. Also covers all 8 `YieldType` values (staking, liquid-staking, restaking, lending, vault, liquidity-pool, concentrated-liquidity-pool, real-world-asset) with mechanics, lock periods, required arguments, typical APY. Accepts optional `yieldType` filter for a single type's section. + +- **Use when:** "how do I filter yields", "how do I sort by APY", "can I query multiple chains at once", "what params does /v1/yields take", "server-side sort", "search yields", "yields_get_all options", "staking vs lending", "lock periods", "what yield types exist", "which type needs validators", "receipt tokens", "concentrated liquidity vs regular liquidity pool", "real-world asset yields". + +- **Backwards-compat:** the old tool name `yield_get_yield_types_guide` is kept as an alias — both names work, same content. ### `yield_troubleshoot_error` -Diagnoses API errors, HTTP status codes, or unexpected responses. For unrecognized errors, automatically looks up the live OpenAPI spec for the authoritative error shape. -**Use when:** user says "I'm getting a 422", "400 error", "rate limited", "transaction failed", "FAILED status", "why did this fail", or pastes any error code / message from the API. +- Diagnoses API errors, HTTP status codes, or unexpected responses. For unrecognized errors, automatically looks up the live OpenAPI spec for the authoritative error shape. + +- **Use when:** user says "I'm getting a 422", "400 error", "rate limited", "transaction failed", "FAILED status", "why did this fail", or pastes any error code / message from the API. ### `yield_list_repos` -Lists the public StakeKit/Yield.xyz GitHub repos that serve as **working code references** for integrations — the staking/yield widget (and its Next.js / Vite reference apps), the **perps widget** (`perps-widget`), the TypeScript/JS SDKs, runnable `api-recipes`, the `signers` and `shield` transaction-security libraries, and an `assets` repo for token/provider logos. Returns each repo with a one-line description and the raw-file fetch pattern (`https://raw.githubusercontent.com/stakekit///`). -**Use when:** the other doc tools don't fully resolve an integration problem and you need to read real, working source code — e.g. "how does the widget wire wallet connect", "show me a runnable enter-position example", "how do I integrate the perps widget", "how does Shield validate a transaction". It points at code; it never returns live data — use the action tools / REST API for that. +- Lists the public StakeKit/Yield.xyz GitHub repos that serve as **working code references** for integrations — the staking/yield widget (and its Next.js / Vite reference apps), the **perps widget** (`perps-widget`), the TypeScript/JS SDKs, runnable `api-recipes`, the `signers` and `shield` transaction-security libraries, and an `assets` repo for token/provider logos. Returns each repo with a one-line description and the raw-file fetch pattern (`https://raw.githubusercontent.com/stakekit///`). + +- **Use when:** the other doc tools don't fully resolve an integration problem and you need to read real, working source code — e.g. "how does the widget wire wallet connect", "show me a runnable enter-position example", "how do I integrate the perps widget", "how does Shield validate a transaction". It points at code; it never returns live data — use the REST API for that. --- -## ❌ Action Tools — **DO NOT USE** During Builder Sessions - -These tools exist to let end-users execute yield flows via an agent. They are **not** a reliable reference for generating integration code because: - -1. **Responses are trimmed** — the MCP strips fields to keep context small. Full REST responses have more. -2. **Responses may be reshaped** — field names / nesting can differ from the raw API. -3. **Code built against MCP responses will be wrong** — users will hit missing fields in production. - -Instead, for **any** data lookup during a build session, use the user's API key against `https://api.yield.xyz` directly (`curl`, `fetch`, `axios`, etc.). - -| Tool | REST equivalent to call instead | -|---|---| -| `yields_get_all` | `GET https://api.yield.xyz/v1/yields?network=&token=&type=&provider=` | -| `yields_get` | `GET https://api.yield.xyz/v1/yields/{yieldId}` | -| `yields_get_validators` | `GET https://api.yield.xyz/v1/yields/{yieldId}/validators` | -| `yields_get_balances` | `POST https://api.yield.xyz/v1/yields/balances` | -| `yields_get_risk` | `GET https://api.yield.xyz/v1/yields/{yieldId}/risk` | -| `yields_get_tvl_history` | `GET https://api.yield.xyz/v1/yields/{yieldId}/tvl/history` | -| `yields_get_reward_rate_history` | `GET https://api.yield.xyz/v1/yields/{yieldId}/reward-rate/history` | -| `actions_enter` | `POST https://api.yield.xyz/v1/actions/enter` | -| `actions_exit` | `POST https://api.yield.xyz/v1/actions/exit` | -| `actions_manage` | `POST https://api.yield.xyz/v1/actions/manage` | -| `actions_get` | `GET https://api.yield.xyz/v1/actions/{actionId}` | -| `actions_get_all` | `GET https://api.yield.xyz/v1/actions` | -| `get_transaction` | `GET https://api.yield.xyz/v1/transactions/{transactionId}` | -| `submit_hash` | `PUT https://api.yield.xyz/v1/transactions/{transactionId}/submit-hash` | -| `networks_get_all` | `GET https://api.yield.xyz/v1/networks` | -| `providers_get_all` | `GET https://api.yield.xyz/v1/providers` | -| `yields_get_kyc_status` | `GET https://api.yield.xyz/v1/yields/{yieldId}/kyc/status` | - -**Exception — the `yields_get` tool for self-documenting schema.** Even here, prefer `yield_get_api_spec` + a live REST call. The REST response carries the full `mechanics.arguments` schema; the MCP response may not. +## ❌ Action Tools — **DO NOT CALL** During Builder Sessions + +The MCP exposes **17 action tools** (listed below). They exist to let an end-user +execute yield flows via an agent — not to let a builder inspect schemas or run flows. + +**In a builder session: never call any of them, and never advise the user to call +them as part of their integration.** Two reasons: + +1. **They're the wrong reference surface for code.** Responses are trimmed/reshaped to + keep agent context small — field names and nesting can differ from the raw API, so + code built against them will be wrong in production. +2. **Execution belongs elsewhere.** Actually running these (entering positions, signing, + broadcasting) carries wallet/security guidance that lives in dedicated skills, not in + a code-generation session. + +**What you _may_ do:** + +- **Explain what a tool does** if the user asks — use the descriptions in the table below. + Explaining ≠ calling. +- **If the user wants to actually run / test action tools**, redirect them to the + dedicated skill (see the redirect table in **SKILL.md → Critical Rule #3**: + `yield-agentkit` for no-wallet exploration, `yield-agentkit-privy` / + `yield-agentkit-moonpay` for execution). + +**For building, get the data from REST instead** — call the live endpoint with the +user's API key (`curl`, `fetch`, `axios`, …) and inspect the full JSON. + +| Tool | What it does | REST equivalent to call instead | +|---|---|---| +| **Discovery** | | | +| `yields_get_all` | Search/filter available yields (paginated summaries) | `GET /v1/yields?network=&token=&type=&provider=` | +| `yields_get` | Full metadata for one yield (mechanics, requirements, arguments schema) | `GET /v1/yields/{yieldId}` | +| `yields_get_validators` | Validator options for delegation-based yields (commission, performance) | `GET /v1/yields/{yieldId}/validators` | +| `networks_get_all` | List all supported networks | `GET /v1/networks` | +| `providers_get_all` | List all yield protocols and validator providers | `GET /v1/providers` | +| **Diligence** | | | +| `yields_get_risk` | Aggregate risk rating for a yield (letter grade + numeric score) | `GET /v1/yields/{yieldId}/risk` | +| `yields_get_reward_rate_history` | Historical APY/reward-rate snapshots over time | `GET /v1/yields/{yieldId}/reward-rate/history` | +| `yields_get_tvl_history` | Historical TVL snapshots over time | `GET /v1/yields/{yieldId}/tvl/history` | +| `yields_get_balances` | Active positions, pending actions, and claimable rewards for a wallet | `POST /v1/yields/balances` | +| `yields_get_kyc_status` | KYC / eligibility status for a wallet against a gated (RWA/permissioned) yield | `GET /v1/yields/{yieldId}/kyc/status` | +| **Execution** | | | +| `actions_enter` | Build unsigned deposit/stake transactions (with gas estimates) | `POST /v1/actions/enter` | +| `actions_exit` | Build unsigned withdraw/unstake transactions (in execution order) | `POST /v1/actions/exit` | +| `actions_manage` | Build unsigned txs for claim, restake, vote, unlock, migrate, etc. | `POST /v1/actions/manage` | +| **Tracking** | | | +| `actions_get` | Status + transaction details for a single action | `GET /v1/actions/{actionId}` | +| `actions_get_all` | List past and pending actions for a wallet (filterable) | `GET /v1/actions` | +| `get_transaction` | Poll a transaction's status (hash, broadcast timestamp) | `GET /v1/transactions/{transactionId}` | +| `submit_hash` | Register a broadcast hash against unsigned transactions for status tracking | `PUT /v1/transactions/{transactionId}/submit-hash` | + +> REST paths are shown relative to the product base URL (`https://api.yield.xyz` for +> Yield). Prepend the base for the product you're integrating. + +**Even for self-documenting schema** (e.g. `yields_get`), prefer `yield_get_api_spec` + +a live REST call. The REST response carries the full `mechanics.arguments` schema; the +MCP response may not. --- @@ -142,9 +181,13 @@ Need to explore docs broadly? → yield_list_doc_topics → yield_lookup_d Hit an error? → yield_troubleshoot_error Need real yield / balance / action data? - → DO NOT use MCP action tools. + → DO NOT call MCP action tools. → Use the user's API key to call the REST endpoint at https://api.yield.xyz directly and inspect the raw JSON. +User wants to actually run/test an + action tool (enter/exit/manage)? → Out of scope for the builder skill. Redirect to a + dedicated skill (yield-agentkit / -privy / -moonpay) + — see SKILL.md → Critical Rule #3. ``` --- From bdc1e4ab127011e2ef381ab9efb30e6a079248c8 Mon Sep 17 00:00:00 2001 From: Ishita Agarwal Date: Wed, 17 Jun 2026 13:33:34 +0530 Subject: [PATCH 04/23] fix: remove exponential.fi occurences --- yield-agentkit-plugin/yield-agentkit/SKILL.md | 2 +- yield-agentkit-skills/skills/yield-agentkit/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yield-agentkit-plugin/yield-agentkit/SKILL.md b/yield-agentkit-plugin/yield-agentkit/SKILL.md index 8707172..e6bb631 100644 --- a/yield-agentkit-plugin/yield-agentkit/SKILL.md +++ b/yield-agentkit-plugin/yield-agentkit/SKILL.md @@ -164,7 +164,7 @@ Perform a management action on an existing position (claim rewards, restake, cha | `status` | `{ enter: boolean, exit: boolean }` | | `mechanics` | Lockup, cooldown, fees, validator requirement, entry limits | | `statistics` | TVL, unique users | -| `risk` | Exponential.fi / Credora ratings | +| `risk` | Staking Rewards / Credora ratings | | `metadata` | Name, logo, description, documentation URL | ### `TransactionDto` — a transaction to execute diff --git a/yield-agentkit-skills/skills/yield-agentkit/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit/SKILL.md index c48f2b6..c5a80ca 100644 --- a/yield-agentkit-skills/skills/yield-agentkit/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit/SKILL.md @@ -181,7 +181,7 @@ Perform a management action on an existing position (claim rewards, restake, cha | `status` | `{ enter: boolean, exit: boolean }` | | `mechanics` | Lockup, cooldown, fees, validator requirement, entry limits | | `statistics` | TVL, unique users | -| `risk` | Exponential.fi / Credora ratings | +| `risk` | Staking Rewards / Credora ratings | | `metadata` | Name, logo, description, documentation URL | ### `TransactionDto` — a transaction to execute From fafe674be4450004904d1b022a6dbe9ed025e2e4 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:02:43 +0200 Subject: [PATCH 05/23] feat(builder-skill): own static guidance + live-prod accuracy audit Migrate the static reference content from the MCP into the skill, rewrite mcp-tools.md routing to the 5 surviving live tools, and narrow detailed builder guidance to Yield (Perps/Borrow now point at the live spec, noting their different action model and submit vs submit-hash). Add dashboard-and-api-keys.md (the API key/dashboard governs which yields & features are enabled) and an RWA/KYC builder flow. Audit every reference against live prod and correct, with evidence: - widget: SKApp + CSS import + theme (not StakeKitWidget); React 19+ - SDK: sdk.configure() singleton (not new Sdk()); add github.com/stakekit/sdk - REST enter body: yieldId/address/arguments (verified against docs.json) - yield types: snake_case enum (+ liquid_staking), high level, DTO as source of truth; drop fabricated statistics.tvlUsd from the response shape - chain args are FLAT in arguments (no additionalAddresses); cosmosPubKey/tezosPubKey flat; bech32 cosmospub1...; validatorAddresses (array) on polkadot/tron; bittensor needs subnetId; cardano validatorAddress required / amount optional - EVM: hex gas/fee fields; per-type args (liquidity_pool amounts[], CLP exit percentage+tokenId, vault receiverAddress/useMaxAllowance); >2-step flows - error envelope {statusCode,timestamp,path,message,validation?,details?}; 412 IS a real code (action blocked); validation=400 not 422; bad/disabled yield=400 not 404 - pagination max limit 100, response key items; sort=rewardRateDesc (no apy sort key) - verified/replaced stale yieldIds; aptos has no live yields; document previously uncovered endpoints (tokens, providers, campaigns, history, kyc/status, submit) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/SKILL.md | 52 ++- .../references/api-field-mapping.md | 68 +++- .../references/api-limits.md | 84 +++++ .../references/chains/aptos.md | 52 +++ .../references/chains/cardano.md | 67 ++++ .../references/chains/cosmos.md | 108 ++++++ .../references/chains/evm.md | 122 ++++++ .../references/chains/near.md | 63 ++++ .../references/chains/solana.md | 171 +++++++++ .../references/chains/stellar.md | 51 +++ .../references/chains/substrate.md | 96 +++++ .../references/chains/sui.md | 56 +++ .../references/chains/tezos.md | 56 +++ .../references/chains/ton.md | 66 ++++ .../references/chains/tron.md | 58 +++ .../references/common-pitfalls.md | 69 +++- .../references/dashboard-and-api-keys.md | 38 ++ .../references/integration-patterns.md | 88 ++++- .../references/mcp-tools.md | 100 +++-- .../references/output-formats.md | 11 +- .../references/policies.md | 97 ++++- .../references/setup.md | 151 +++++++- .../references/signing-patterns.md | 106 +++++- .../references/transaction-lifecycle.md | 350 ++++++++++++++++++ .../references/yield-types.md | 158 ++++++++ 25 files changed, 2241 insertions(+), 97 deletions(-) create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 901369f..73dc097 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -1,6 +1,6 @@ --- name: yield-agentkit-builder -description: Build applications that integrate with the Yield.xyz APIs across all three products — Yield (staking, lending, vaults, RWA across 80+ networks), Perps (perpetual futures on Hyperliquid), and Borrow (lending/borrowing markets). Generates production-ready code covering REST API integration, transaction signing, wallet connection, and fee monetization. Use when user wants to build an app, integrate yield/perps/borrow, generate code, or set up a project using Yield.xyz. +description: Build applications that integrate with the Yield.xyz APIs. Detailed builder guidance in this skill is Yield-only — staking, lending, vaults, and RWA across 80+ networks. Perps (perpetual futures on Hyperliquid) and Borrow (lending/borrowing markets) are supported via the live spec (`yield_get_api_spec({ product })`) but use a different action model. Generates production-ready code covering REST API integration, transaction signing, wallet connection, and fee monetization. Use when user wants to build an app, integrate yield/perps/borrow, generate code, or set up a project using Yield.xyz. metadata: author: Yield.xyz version: "1.0.0" @@ -13,6 +13,16 @@ This skill helps developers build applications that integrate with the Yield.xyz It generates production-ready code, guides architecture decisions, and ensures correct API usage across all supported chains and protocols. +**Scope: this skill's detailed builder guidance is Yield-only** — staking, lending, +vaults, and RWA across 80+ networks. Perps and Borrow are still supported, but you +build them straight off the live spec — `yield_get_api_spec({ product: "perps" | "borrow" })` — +rather than from the worked patterns here, because they use a **different action model**: +a single `POST /v1/actions` with a `type` discriminator (not the Yield +`enter` / `exit` / `manage` endpoints), and they submit the signed transaction with +`POST /v1/transactions/{id}/submit` — **not** `submit-hash`. Everywhere below, the +detailed flows, reference files, and submit-hash lifecycle describe **Yield**; for +Perps/Borrow, lean on the live spec and keep those two differences in mind. + --- ## When This Skill Activates @@ -203,8 +213,9 @@ must ship with pagination wired in from the start. 1. **Page size** — default to 20–50 items per page; never render the full list at once. 2. **Use the API's built-in pagination params** on `GET /v1/yields` (`limit`, - `offset` or cursor — check `yield_get_api_spec` for the current shape). Do - **not** fetch everything client-side and slice — that defeats the point. + `offset` — the API is offset-only, max `limit` is 100; check `yield_get_api_spec` + for the current shape). Do **not** fetch everything client-side and slice — that + defeats the point. 3. **Provide a way to change pages** — next/prev buttons, numbered pager, or an infinite-scroll sentinel (whichever fits the UI). 4. **Preserve filters across pages** — network, token, type, provider filters @@ -240,9 +251,11 @@ whenever the API supports it. **The very first action in every builder session** — before asking for an API key, before asking what the user is building — is to ensure the `yield-agentkit` MCP server is registered with the user's agent. This is **not optional**. The skill's -doc tools (`yield_get_api_spec`, `yield_get_chain_guide`, -`yield_get_transaction_guide`, etc.) come from that MCP server, and without them the -skill will generate code from memory rather than from the live spec. +live tools (`yield_get_api_spec`, `yield_lookup_docs`, `yield_fetch_doc`, +`yield_troubleshoot_error`, `yield_list_repos`) come from that MCP server, and without +them the skill will generate code from memory rather than from the live spec. (Static +guidance — chains, transaction lifecycle, yield types, safety — lives in this skill's +own `references/` files, not on the MCP.) **Do this automatically — do not ask the user to run the command themselves.** Run the appropriate registration command for their agent, then verify it connected. @@ -359,7 +372,10 @@ Generate code that: 1. Uses the chosen product's base URL (`api.yield.xyz`, `perps.yield.xyz`, or `borrow.yield.xyz`) 2. Passes the user's API key via `x-api-key` header 3. Uses field names exactly as seen in the live spec and API responses -4. Handles the full transaction lifecycle (action -> sign -> broadcast -> submit-hash) +4. Handles the full transaction lifecycle. **For Yield, always report the hash after + broadcasting** via `PUT /v1/transactions/{txId}/submit-hash` (action -> sign -> + broadcast -> submit-hash). **Perps and Borrow do not use `submit-hash`** — they + submit the signed transaction with `POST /v1/transactions/{id}/submit` instead. 5. Follows the signing pattern appropriate for the user's product type and wallet choice See **[`references/signing-patterns.md`](./references/signing-patterns.md)** for @@ -429,17 +445,29 @@ before generating code for that topic.** | **[`references/output-formats.md`](./references/output-formats.md)** | When generating UI code — display rules, number formatting | | **[`references/policies.md`](./references/policies.md)** | API usage limits, rate limiting, caching guidance | | **[`references/setup.md`](./references/setup.md)** | First-time setup — prerequisites | +| **[`references/dashboard-and-api-keys.md`](./references/dashboard-and-api-keys.md)** | How the dashboard/API key controls which yields & features are enabled — read when a yield "isn't available" or returns `400 not enabled` | +| **[`references/yield-types.md`](./references/yield-types.md)** | `GET /v1/yields` query params + the yield-type categories (high level; DTO is source of truth) | +| **[`references/api-limits.md`](./references/api-limits.md)** | Rate limits, key tiers, throttling | +| **[`references/transaction-lifecycle.md`](./references/transaction-lifecycle.md)** | End-to-end **Yield** transaction flow: build → sign → broadcast → submit-hash → confirm. (Perps/Borrow use `POST /v1/transactions/{id}/submit` instead of `submit-hash`.) | --- ## SDK Option -For TypeScript/JavaScript projects, the `@yieldxyz/sdk` package wraps the REST API: +For TypeScript/JavaScript projects, the `@yieldxyz/sdk` package wraps the REST API. +It's a configured singleton — `configure()` once, then call `sdk.api.*`: ```typescript -import { Sdk } from "@yieldxyz/sdk"; -const sdk = new Sdk({ apiKey: process.env.YIELD_API_KEY }); +import { sdk } from "@yieldxyz/sdk"; +sdk.configure({ apiKey: process.env.YIELD_API_KEY }); + +const yields = await sdk.api.getYields({ network: "ethereum" }); ``` -For other languages (Python, Go, Rust), generate REST API calls directly. -Refer to `https://api.yield.xyz/docs.json` for the complete OpenAPI spec. +Source: [github.com/stakekit/sdk](https://github.com/stakekit/sdk) (published as `@yieldxyz/sdk`). + +For other languages (Python, Go, Rust), or any non-TypeScript agent, skip the SDK and +call the REST API directly — every endpoint is plain HTTP. Refer to +`https://api.yield.xyz/docs.json` for the complete OpenAPI spec (or the `yield_get_api_spec` +tool), and see [github.com/stakekit/api-recipes](https://github.com/stakekit/api-recipes) +for runnable REST examples. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md index 84a0f3a..3d0d802 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md @@ -57,6 +57,12 @@ These are the API paths. For exact request/response schemas, always check `docs. | `/v1/yields/balances` | POST | Positions and pending actions for a wallet | | `/v1/networks` | GET | List all supported networks | +> **Yield object shape.** A yield item's real top-level keys are: +> `id, network, chainId, inputTokens, token, tokens, rewardRate, status, metadata, +> mechanics, providerId, prime, outputToken, tags, state`. +> There is **no top-level `apy`** — the reward rate lives in `rewardRate`. There is +> **no top-level `risk`** field — risk comes from `GET /v1/yields/{id}/risk`. + ### Actions | Endpoint | Method | Purpose | @@ -64,8 +70,35 @@ These are the API paths. For exact request/response schemas, always check `docs. | `/v1/actions/enter` | POST | Build enter (deposit/stake) transactions | | `/v1/actions/exit` | POST | Build exit (withdraw/unstake) transactions | | `/v1/actions/manage` | POST | Build manage (claim/restake) transactions | -| `/v1/transactions/{txId}/submit-hash` | PUT | Report broadcast hash (mandatory after every tx) | +| `/v1/actions` | GET | List actions (requires `address`) | +| `/v1/actions/{actionId}` | GET | Single action by id (UUID) | + +### Transactions + +| Endpoint | Method | Purpose | +|---|---|---| | `/v1/transactions/{txId}` | GET | Check transaction status | +| `/v1/transactions/{txId}/submit-hash` | PUT | Report the on-chain broadcast hash. Body: `{ "hash": "…" }`. Use this when **you** broadcast the signed tx yourself. Mandatory after every self-broadcast | +| `/v1/transactions/{txId}/submit` | POST | **Distinct from submit-hash.** Hand the **signed transaction** to Yield.xyz and let *it* broadcast. Body: `{ "signedTransaction": "…" }`. Do not call both for the same tx | + +### Discovery & History (reference) + +Additional live endpoints, useful for discovery and analytics. History endpoints return +`404` for yields that aren't indexed. + +| Endpoint | Method | Purpose | +|---|---|---| +| `/v1/tokens` | GET | List supported tokens | +| `/v1/providers` | GET | List protocol/validator providers | +| `/v1/providers/{id}` | GET | Single provider metadata | +| `/v1/yields/{id}/campaigns` | GET | Reward campaigns for a yield | +| `/v1/yields/{id}/balances` | GET | Balances for a single yield (query: `address`) | +| `/v1/yields/{id}/balances/history` | GET | Balance history (indexed yields only) | +| `/v1/yields/{id}/rewards/history` | GET | Reward history (indexed yields only) | +| `/v1/yields/{id}/reward-rate/history` | GET | Reward-rate history | +| `/v1/yields/{id}/tvl/history` | GET | TVL history | +| `/v1/yields/{id}/risk` | GET | Risk profile for the yield (risk is **not** a field on the yield object) | +| `/v1/yields/{id}/kyc/status` | GET | KYC requirement/status for the yield | --- @@ -106,11 +139,36 @@ x-api-key: YOUR_API_KEY ## Common Error Status Codes +The set of status codes the **application** returns is `400, 401, 403, 404, 412, 422, 429, 500`. +A transient `502`/`503` can still reach you from the edge/infra layer (CDN, load balancer) +rather than the application — treat those as retryable with backoff (see `policies.md`). + | Code | Meaning | |---|---| -| `400` | Bad input — check field names, required fields, amount format | +| `400` | Bad input — failed validation, bad field names, bad amount format. **Also returned for an unknown or disabled `yieldId`** (`"… is not enabled for this project"`) — *not* `404` | | `401` | Missing or invalid API key | -| `404` | Yield or transaction not found | -| `412` | Precondition failed — yield not available, below minimum, etc. | +| `403` | Forbidden — key lacks access to the requested resource | +| `404` | Resource not found (e.g. history endpoints for a yield that isn't indexed) | +| `412` | **Precondition failed — the action is blocked right now**, not malformed: yield closed for deposits (`status.enter:false`) or withdrawals (`status.exit:false`), yield blocked, or resubmitting a different hash to a terminal transaction. Check `status.enter`/`status.exit` from the yield DTO before building an action | +| `422` | Unprocessable entity | | `429` | Rate limited — respect `retry-after` header | -| `503` | Upstream service unavailable — retry with backoff | +| `500` | Internal server error | + +### Error Envelope + +Every error response uses this shape: + +```json +{ + "statusCode": 400, + "timestamp": "2026-06-17T08:38:47.236Z", + "path": "/v1/actions/enter", + "message": "Bad Request Exception", + "validation": { "message": ["yieldId must be a string"] }, + "details": { "error": "Bad Request" } +} +``` + +There is **no top-level `error` field.** `validation` (with a `message[]` array) and +`details` are optional and present only when relevant. Validation failures are `400` +with `validation.message[]`, not `422`. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md new file mode 100644 index 0000000..6bcbac9 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md @@ -0,0 +1,84 @@ +# API Rate Limits + +## Rate Limit Tiers + +| Tier | Requests/min | Requests/day | Use Case | +|------|-------------|-------------|----------| +| Free/Test | 30 | 1,000 | Development, testing, prototyping | +| Production | 300 | 50,000 | Production applications | +| Enterprise | Custom | Custom | High-volume integrations | + +## How Rate Limits Work + +- Limits are applied per API key +- The `x-ratelimit-remaining` header shows remaining requests +- The `x-ratelimit-reset` header shows when the limit resets (Unix timestamp) +- When exceeded, the API returns `429 Too Many Requests` +- The `retry-after` header tells you how many seconds to wait + +## Best Practices + +### Implement Exponential Backoff + +```typescript +async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) { + for (let attempt = 0; attempt < maxRetries; attempt++) { + const response = await fetch(url, options); + + if (response.status === 429) { + const retryAfter = parseInt(response.headers.get("retry-after") || "5"); + const delay = retryAfter * 1000 * Math.pow(2, attempt); + await new Promise(resolve => setTimeout(resolve, delay)); + continue; + } + + return response; + } + throw new Error("Max retries exceeded"); +} +``` + +### Cache Yield Metadata + +Yield metadata (`GET /v1/yields/{id}`) changes infrequently. Cache it for 5-15 minutes to reduce API calls. + +```typescript +const yieldCache = new Map(); + +async function getYieldCached(yieldId: string) { + const cached = yieldCache.get(yieldId); + if (cached && Date.now() < cached.expiry) return cached.data; + + const data = await sdk.api.getYield(yieldId); + yieldCache.set(yieldId, { data, expiry: Date.now() + 10 * 60 * 1000 }); + return data; +} +``` + +### Batch Requests Where Possible + +Use `GET /v1/yields` with filters instead of fetching individual yields: +``` +GET /v1/yields?network=ethereum&token=USDC +``` + +### Use Webhooks for Status Updates + +Instead of polling transaction status, configure webhooks to receive status change notifications. + +## Getting a Production Key + +1. Sign up at https://dashboard.yield.xyz +2. Create a project +3. Generate an API key +4. Set the key in your requests: `x-api-key: YOUR_KEY` + +## Monitoring Usage + +Check your current usage and limits in the Yield.xyz dashboard at https://docs.yield.xyz/docs/rate-limits-and-plans + +## Need Higher Limits? + +Contact the Yield.xyz team: +- Email: hello@yield.xyz +- Dashboard: https://dashboard.yield.xyz diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md new file mode 100644 index 0000000..1940f3a --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md @@ -0,0 +1,52 @@ +# Aptos Integration Guide + +> **No live yields currently.** Aptos has 0 live yields — `aptos-apt-native-staking` returns HTTP 400 and is not integrable today. The signing reference below is kept for when a yield goes live, but there is nothing to integrate yet. + +## unsignedTransaction Format + +**Encoding:** JSON object with transaction payload +**Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` if string + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"10"` = 10 APT | +| `validatorAddress` | Yes (native staking) | Get from `GET /v1/yields/{id}/validators` | + +## Signing + +```typescript +import { AptosClient, AptosAccount } from "aptos"; + +const client = new AptosClient("https://fullnode.mainnet.aptoslabs.com"); +const account = new AptosAccount(privateKeyBytes); + +for (const tx of action.transactions) { + const payload = JSON.parse(tx.unsignedTransaction); + + // Sign and submit + const txnRequest = await client.generateTransaction(account.address(), payload); + const signedTxn = await client.signTransaction(account, txnRequest); + const result = await client.submitTransaction(signedTxn); + await client.waitForTransaction(result.hash); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash: result.hash }); +} +``` + +## Common Gotchas + +1. **Move-based**: Aptos uses the Move language. Transaction payloads reference Move modules and functions. + +2. **Sequence number**: Each account has a sequence number that must match. The API handles this. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=aptos" \ + -H "x-api-key: YOUR_KEY" +``` + +No live yieldIds currently. `aptos-apt-native-staking` returns HTTP 400. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md new file mode 100644 index 0000000..e0b355f --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md @@ -0,0 +1,67 @@ +# Cardano Integration Guide + +## unsignedTransaction Format + +**Encoding:** Hex-encoded CBOR transaction bytes +**Parse before signing:** No — hex decode and use Cardano serialization library + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `validatorAddress` | Yes | Stake pool ID from `GET /v1/yields/{id}/validators` | +| `amount` | No | Optional — delegation stakes the whole account, so an explicit amount is not required | + +Confirm exact required fields via `mechanics.arguments.enter.fields[]` in the yield DTO. + +## Signing + +```typescript +import * as CardanoWasm from "@emurgo/cardano-serialization-lib-nodejs"; + +for (const tx of action.transactions) { + const txBytes = Buffer.from(tx.unsignedTransaction, "hex"); + const transaction = CardanoWasm.Transaction.from_bytes(txBytes); + + // Get the transaction body hash for signing + const txHash = CardanoWasm.hash_transaction(transaction.body()); + + // Sign + const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new(); + const vkeyWitness = CardanoWasm.make_vkey_witness(txHash, privateKey); + vkeyWitnesses.add(vkeyWitness); + + const witnessSet = CardanoWasm.TransactionWitnessSet.new(); + witnessSet.set_vkeys(vkeyWitnesses); + + const signedTx = CardanoWasm.Transaction.new( + transaction.body(), + witnessSet, + transaction.auxiliary_data() + ); + + // Broadcast via Cardano node + const hash = await submitTransaction(signedTx.to_bytes()); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash }); +} +``` + +## Common Gotchas + +1. **UTXO model**: Cardano uses UTXOs, not accounts. The API handles UTXO selection. + +2. **Stake registration**: First-time stakers need a stake key registration transaction before delegation. The API includes this automatically. + +3. **Pool saturation**: Delegating to saturated pools reduces rewards. Use `GET /v1/yields/{id}/validators` for pool metrics. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=cardano" \ + -H "x-api-key: YOUR_KEY" +``` + +Common yieldIds: +- `cardano-ada-native-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md new file mode 100644 index 0000000..be200e4 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md @@ -0,0 +1,108 @@ +# Cosmos Integration Guide + +Covers: Cosmos Hub (ATOM), Osmosis, Celestia, dYdX, Injective, Sei, and all Cosmos SDK chains. + +## unsignedTransaction Format + +**Encoding:** Hex-encoded Protobuf SignDoc bytes (string) +**Parse before signing:** No — hex decode and pass directly to Cosmos signing SDK + +The API returns a hex string like: +``` +"0a92010a8f010a2f2f636f736d6f732e7374616b696e672e763162657461312e4d736744656c6567617465125c0a2d..." +``` + +This encodes a Protobuf `SignDoc` containing: +- `bodyBytes`: Encoded TxBody (messages, memo, timeout) +- `authInfoBytes`: Encoded AuthInfo (signer, fee, gas) +- `chainId`: e.g. "cosmoshub-4" +- `accountNumber`: Account number on chain + +## Required Arguments + +When calling `POST /v1/actions/enter` for any Cosmos yield, the schema (`mechanics.arguments.enter`) requires: + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"100"` = 100 ATOM | +| `validatorAddress` | Yes (native staking) | Get from `GET /v1/yields/{id}/validators` | +| `cosmosPubKey` | Yes | Cosmos public key in **bech32** format (`cosmospub1...`). Flat field inside `arguments`. | + +`exit` requires the same fields — `amount`, `validatorAddress`, and `cosmosPubKey`. Confirm exact required fields via `mechanics.arguments.enter.fields[]` / `mechanics.arguments.exit.fields[]` in the yield DTO. + +### Getting cosmosPubKey + +```typescript +// Using @cosmjs/proto-signing +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; + +const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "cosmos" }); +const [account] = await wallet.getAccounts(); +// The API expects the bech32-encoded public key (cosmospub1...), not hex/base64. +const cosmosPubKey = wallet.encodePubkey + ? wallet.encodePubkey(account.pubkey) + : /* bech32-encode account.pubkey with the "cosmospub" prefix */ undefined; +``` + +```typescript +// Using @yieldxyz/sdk +const action = await sdk.api.enterYield({ + yieldId: "cosmos-atom-native-staking", + address: "cosmos1abc...", + arguments: { + amount: "100", + validatorAddress: "cosmosvaloper1xyz...", + cosmosPubKey: "cosmospub1addwnpepq...", + }, +}); +``` + +## Signing + +```typescript +import { SigningStargateClient } from "@cosmjs/stargate"; + +for (const tx of action.transactions) { + // tx.unsignedTransaction is hex-encoded SignDoc bytes + const signDocBytes = Buffer.from(tx.unsignedTransaction, "hex"); + + // Sign with your Cosmos signer + const signed = await signer.signDirect(address, decodeSignDoc(signDocBytes)); + + // Broadcast + const result = await client.broadcastTx(signed); + + // Submit hash back to Yield.xyz — MANDATORY + await fetch(`https://api.yield.xyz/v1/transactions/${tx.id}/submit-hash`, { + method: "PUT", + headers: { "Content-Type": "application/json", "x-api-key": API_KEY }, + body: JSON.stringify({ hash: result.transactionHash }), + }); + + // Wait for confirmation before next stepIndex +} +``` + +## Common Gotchas + +1. **cosmosPubKey missing → 422 error**: The most common Cosmos error. Always include the flat `cosmosPubKey` field (bech32 `cosmospub1...`) — there is no `additionalAddresses` wrapper. + +2. **21-day unbonding period**: Native ATOM staking has a 21-day unbonding. After `actions_exit`, the balance shows `type: "exiting"` for 21 days before becoming `type: "withdrawable"`. + +3. **Validator changes**: Don't hardcode validator addresses. Validators can change commission, get jailed, or go offline. Always use `GET /v1/yields/{id}/validators` at runtime. + +4. **Multiple Cosmos chains**: Osmosis, Celestia, dYdX, Injective, Sei all use the same Cosmos SignDoc format but different chain IDs and potentially different required arguments. Always read `mechanics.arguments` for the specific yield. + +## Available Yields + +```bash +# Discover all Cosmos yields +curl "https://api.yield.xyz/v1/yields?network=cosmos" \ + -H "x-api-key: YOUR_KEY" +``` + +Common Cosmos yieldIds: +- `cosmos-atom-native-staking` +- `osmosis-osmo-native-staking` +- `celestia-tia-native-staking` +- `dydx-dydx-native-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md new file mode 100644 index 0000000..d061e19 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md @@ -0,0 +1,122 @@ +# EVM Integration Guide + +Covers: Ethereum, Base, Arbitrum, Optimism, Polygon, Avalanche, BSC, Linea, Sonic, Gnosis, Celo, and all other EVM-compatible networks. + +## unsignedTransaction Format + +**Encoding:** JSON string +**Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` + +```json +{ + "from": "0xUserWallet", + "to": "0xContractAddress", + "data": "0xEncodedCallData...", + "gasLimit": "0xdd94", + "nonce": 42, + "chainId": 8453, + "maxFeePerGas": "0x02627940", + "maxPriorityFeePerGas": "0x00", + "type": 2 +} +``` + +Note: gas/fee fields (`gasLimit`, `maxFeePerGas`, `maxPriorityFeePerGas`) are **hex strings**, while `nonce`, `type`, and `chainId` are JSON numbers. `value` is **absent** for token operations (it only appears for native-token transfers). + +| Field | Description | +|-------|-------------| +| `from` | Sender address | +| `to` | Contract or recipient | +| `data` | Hex-encoded calldata (contract function + args) | +| `value` | Wei amount as a hex string — present only for native-token transfers; absent for ERC-20/token operations | +| `gasLimit` | Gas limit (hex string, e.g. `"0xdd94"`) | +| `maxFeePerGas` | EIP-1559 max fee (hex string) | +| `maxPriorityFeePerGas` | EIP-1559 priority fee (hex string, e.g. `"0x00"`) | +| `nonce` | Account nonce (JSON number) | +| `chainId` | JSON number. 1=Ethereum, 8453=Base, 42161=Arbitrum, 10=Optimism, 137=Polygon | +| `type` | JSON number. 0=legacy, 2=EIP-1559 | + +## Required Arguments + +Arguments vary by yield **type** — do not assume every yield takes only `amount`: + +| Yield type | Enter arguments | Exit arguments | +|------------|-----------------|----------------| +| lending / staking / restaking / vault | `amount` (human-readable, `"100"` = 100 USDC) | varies (e.g. `amount`) | +| `liquidity_pool` | `amounts` — an **ARRAY** (one entry per pool token, e.g. `["1.0", "1000"]`), not `amount` | — | +| `concentrated_liquidity_pool` | `amount` + optional `rangeMin`/`rangeMax` (decimal-string price bounds — omit both for a full-range position) + optional `inputToken`/`inputTokenNetwork` | `percentage` (0–100) **and** `tokenId` (position NFT id) — **both required, NO `amount`** | + +Type-specific notes: +- **vault enter:** optional `receiverAddress`, `useMaxAllowance` (bool). **vault exit:** optional `useMaxAmount` (bool); `amount` optional. +- **restaking enter:** may expose `inputToken` (with an options list) and `minimum`. + +There is no `cosmosPubKey` equivalent on EVM, but the argument set is still per-yield. **Always confirm the exact fields in `mechanics.arguments.enter.fields[]` (and `.exit.fields[]`) from the yield DTO before building an action.** + +## Signing + +```typescript +// Using ethers.js v6 +import { ethers } from "ethers"; + +const provider = new ethers.JsonRpcProvider(RPC_URL); +const wallet = new ethers.Wallet(PRIVATE_KEY, provider); + +for (const tx of action.transactions) { + const parsed = JSON.parse(tx.unsignedTransaction); + + // Sign and send — DO NOT MODIFY any field + const txResponse = await wallet.sendTransaction(parsed); + const receipt = await txResponse.wait(); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { + hash: receipt.hash, + }); +} +``` + +```typescript +// Using viem +import { createWalletClient, http } from "viem"; +import { base } from "viem/chains"; + +const client = createWalletClient({ chain: base, transport: http() }); + +for (const tx of action.transactions) { + const parsed = JSON.parse(tx.unsignedTransaction); + const hash = await client.sendTransaction(parsed); + + await sdk.api.submitTransactionHash(tx.id, { hash }); +} +``` + +## Multi-Step Transactions + +EVM actions often return multiple transactions — and there can be **more than two**. A typical lending entry is: + +1. **APPROVAL** (stepIndex: 0) — approve the contract to spend your tokens +2. **SUPPLY/STAKE** (stepIndex: 1) — the actual deposit + +A two-sided concentrated-liquidity-pool entry returns three: **APPROVAL + APPROVAL + ADD_LIQUIDITY** (one approval per pool token, then the add). + +Transaction `type` values seen live include: `APPROVAL`, `SUPPLY`, `STAKE`, `ADD_LIQUIDITY`, `WITHDRAW`, `UNSTAKE`. + +Always execute in `stepIndex` order. Wait for `CONFIRMED` before proceeding. + +## Common Gotchas + +1. **Token approval step**: Most ERC-20 yield entries require a prior approval transaction. The API includes this automatically — just process all transactions in order. + +2. **chainId mismatch**: Your signer must be connected to the same chain as `tx.network`. Base = 8453, Arbitrum = 42161, etc. + +3. **Gas estimation**: The API provides `gasEstimate` in the response. You can use it or let your provider estimate. Don't manually set gas too low. + +## Available Yields + +EVM has the widest yield selection: lending (Aave, Morpho, Compound), liquid staking (Lido, Rocketpool), vaults (Euler, Yearn), and more. + +```bash +# USDC lending on Base +curl "https://api.yield.xyz/v1/yields?network=base&token=USDC" \ + -H "x-api-key: YOUR_KEY" +``` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md new file mode 100644 index 0000000..6f47316 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md @@ -0,0 +1,63 @@ +# Near Integration Guide + +## unsignedTransaction Format + +**Encoding:** JSON string with transaction object +**Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"10"` = 10 NEAR | +| `validatorAddress` | Yes (native staking) | Get from `GET /v1/yields/{id}/validators` | + +## Signing + +```typescript +import { connect, keyStores, KeyPair } from "near-api-js"; + +const keyStore = new keyStores.InMemoryKeyStore(); +await keyStore.setKey("mainnet", accountId, KeyPair.fromString(privateKey)); + +const near = await connect({ + networkId: "mainnet", + keyStore, + nodeUrl: "https://rpc.mainnet.near.org", +}); + +const account = await near.account(accountId); + +for (const tx of action.transactions) { + const txData = JSON.parse(tx.unsignedTransaction); + + // Execute the transaction + const result = await account.signAndSendTransaction({ + receiverId: txData.receiverId, + actions: txData.actions, + }); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { + hash: result.transaction.hash, + }); +} +``` + +## Common Gotchas + +1. **Account model**: NEAR uses named accounts (e.g., `alice.near`), not hex addresses. + +2. **Storage deposit**: Some NEAR staking operations require a storage deposit. The API includes this in the transactions. + +3. **4-epoch unbonding**: NEAR native staking has a ~52-hour unbonding period (4 epochs). + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=near" \ + -H "x-api-key: YOUR_KEY" +``` + +Common yieldIds: +- `near-near-native-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md new file mode 100644 index 0000000..e34b680 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md @@ -0,0 +1,171 @@ +# Solana Integration Guide + +## unsignedTransaction Format + +**Encoding:** Hex or base64 encoded serialized transaction +**Parse before signing:** No — decode and pass to Solana signing SDK + +The API returns the transaction as a hex-encoded string. You need to: +1. Hex decode to bytes +2. Deserialize into a `Transaction` or `VersionedTransaction` +3. Sign with your keypair +4. Broadcast + +## Yield Types + +Solana is overwhelmingly **lending + vault** (~251 yields total, mostly lending/vault), not staking-only. Required arguments depend on the yield type — always confirm via `mechanics.arguments.enter.fields[]` in the yield DTO. + +| Yield type | Enter args | Exit args | +|------------|-----------|-----------| +| Lending / vault | `amount` (required) | `amount` (required); optional `receiverAddress`, `useInstantExecution` | +| Native multivalidator staking (`requiresValidatorSelection=true`) | `amount` + `validatorAddress` | `amount` + `validatorAddress` | +| Marinade liquid staking (`requiresValidatorSelection=false`) | `amount` | `amount`; optional `useInstantExecution` | + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"1"` = 1 SOL | +| `validatorAddress` | Yes (native multivalidator staking only) | Get from `GET /v1/yields/{id}/validators` | + +## Signing + +```typescript +import { Connection, Transaction, Keypair } from "@solana/web3.js"; +import bs58 from "bs58"; + +const connection = new Connection("https://api.mainnet-beta.solana.com"); +const keypair = Keypair.fromSecretKey(/* your key */); + +for (const tx of action.transactions) { + // Decode the hex-encoded transaction + const txBytes = Buffer.from(tx.unsignedTransaction, "hex"); + const transaction = Transaction.from(txBytes); + + // Sign + transaction.sign(keypair); + + // Broadcast + const signature = await connection.sendRawTransaction(transaction.serialize()); + await connection.confirmTransaction(signature); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash: signature }); +} +``` + +### Versioned Transactions + +Some Solana yields use Versioned Transactions (v0). Handle both: + +```typescript +import { VersionedTransaction } from "@solana/web3.js"; + +const txBytes = Buffer.from(tx.unsignedTransaction, "hex"); + +try { + // Try versioned first + const vtx = VersionedTransaction.deserialize(txBytes); + vtx.sign([keypair]); + const sig = await connection.sendRawTransaction(vtx.serialize()); +} catch { + // Fall back to legacy + const ltx = Transaction.from(txBytes); + ltx.sign(keypair); + const sig = await connection.sendRawTransaction(ltx.serialize()); +} +``` + +## Browser Wallet Signing (Phantom) + +> **Use this section when signing in the browser with Phantom's Solana wallet (`window.phantom.solana`).** +> This is completely different from EVM signing — do NOT use `ethers.js` or `BrowserProvider` for Solana. + +Phantom exposes a Solana-specific API at `window.phantom.solana`. It accepts a deserialized `VersionedTransaction` or `Transaction` object and returns the signature. + +```typescript +import { Connection, VersionedTransaction, Transaction } from "@solana/web3.js"; + +const phantomSolana = window.phantom?.solana; +if (!phantomSolana?.isPhantom) throw new Error("Phantom Solana wallet not found"); + +// Connect if not already connected +await phantomSolana.connect(); + +const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed"); + +for (const tx of action.transactions.sort((a, b) => a.stepIndex - b.stepIndex)) { + // Decode hex-encoded transaction bytes + const txBytes = Buffer.from(tx.unsignedTransaction, "hex"); + + let signature: string; + try { + // Try VersionedTransaction first — most modern Solana programs use v0 + let transaction: VersionedTransaction | Transaction; + try { + transaction = VersionedTransaction.deserialize(txBytes); + } catch { + transaction = Transaction.from(txBytes); + } + + // Phantom signs AND sends — returns { signature } + const result = await phantomSolana.signAndSendTransaction(transaction); + signature = result.signature; + } catch (err) { + // Phantom throws { code, message } plain objects (not Error instances) + const msg = (err as any)?.message ?? JSON.stringify(err); + throw new Error(`Phantom signing failed: ${msg}`); + } + + // Wait for confirmation + const { value: status } = await connection.confirmTransaction(signature, "confirmed"); + if (status.err) throw new Error(`Transaction failed on-chain: ${JSON.stringify(status.err)}`); + + // Submit hash — MANDATORY + await fetch(`/v1/transactions/${tx.id}/submit-hash`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ hash: signature }), + }); +} +``` + +**Key differences from EVM browser wallet:** +- No `ethers.js` — Phantom Solana has its own API +- `signAndSendTransaction()` signs AND broadcasts in one call — no separate broadcast step +- Returns `{ signature }` not `{ hash }` — the signature IS the transaction ID on Solana +- Do NOT strip any fields — pass the deserialized transaction object directly +- Phantom throws plain `{ code, message }` objects on error, same pattern as MetaMask + +**`window.phantom.solana` vs `window.phantom.ethereum`:** + +| | `phantom.solana` | `phantom.ethereum` | +|---|---|---| +| Chain | Solana | EVM (same as MetaMask) | +| Signing method | `signAndSendTransaction(VersionedTransaction)` | `eth_sendTransaction` via EIP-1193 | +| Returns | `{ signature: string }` | transaction hash | +| Library | `@solana/web3.js` | `ethers.js BrowserProvider` | + +## Common Gotchas + +1. **Hex vs Base64**: The API typically returns hex. If you get base64, decode with `Buffer.from(tx.unsignedTransaction, "base64")`. + +2. **Blockhash expiry**: Solana transactions include a recent blockhash that expires after ~2 minutes. If signing takes too long, request a new action. + +3. **Versioned transactions**: Some protocols require v0 transactions with address lookup tables. Use `VersionedTransaction.deserialize()`. + +4. **Confirmation level**: Use `"confirmed"` commitment for faster feedback, `"finalized"` for maximum safety. + +## Available Yields + +```bash +# Discover Solana yields +curl "https://api.yield.xyz/v1/yields?network=solana" \ + -H "x-api-key: YOUR_KEY" +``` + +Common Solana staking yieldIds: +- `solana-sol-native-multivalidator-staking` (native staking; `requiresValidatorSelection=true`; enter `amount` + `validatorAddress`) +- `solana-sol-marinade-staking` (liquid staking; `requiresValidatorSelection=false`; enter `amount`) + +Most Solana yields are lending or vault — discover them with the query above. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md new file mode 100644 index 0000000..c5c8d9a --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md @@ -0,0 +1,51 @@ +# Stellar Integration Guide + +## unsignedTransaction Format + +**Encoding:** Base64-encoded XDR transaction envelope +**Parse before signing:** No — base64 decode and use Stellar SDK + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"100"` = 100 XLM | + +## Signing + +```typescript +import * as StellarSdk from "stellar-sdk"; + +const server = new StellarSdk.Horizon.Server("https://horizon.stellar.org"); +const keypair = StellarSdk.Keypair.fromSecret(secretKey); + +for (const tx of action.transactions) { + // Decode the XDR envelope + const transaction = StellarSdk.TransactionBuilder.fromXDR( + tx.unsignedTransaction, + StellarSdk.Networks.PUBLIC + ); + + // Sign + transaction.sign(keypair); + + // Broadcast + const result = await server.submitTransaction(transaction); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash: result.hash }); +} +``` + +## Common Gotchas + +1. **XDR format**: Stellar uses XDR (External Data Representation) for transaction serialization. + +2. **Network passphrase**: Use `StellarSdk.Networks.PUBLIC` for mainnet, `StellarSdk.Networks.TESTNET` for testnet. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=stellar" \ + -H "x-api-key: YOUR_KEY" +``` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md new file mode 100644 index 0000000..543f9b5 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md @@ -0,0 +1,96 @@ +# Substrate Integration Guide + +Covers: Polkadot (DOT), Bittensor (TAO) + +Substrate live networks are only `polkadot` and `bittensor`. + +## unsignedTransaction Format + +**Encoding:** JSON object with call data +**Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` if string + +The API returns a JSON object containing the extrinsic data: + +```json +{ + "method": "0x...", + "era": "0x...", + "nonce": "0x...", + "tip": "0x...", + "specVersion": "0x...", + "transactionVersion": "0x...", + "genesisHash": "0x...", + "blockHash": "0x..." +} +``` + +## Required Arguments + +### Polkadot (`polkadot-dot-validator-staking`) + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"250"` = 250 DOT | +| `validatorAddresses` | Yes (native staking) | **Array (plural)**. Get from `GET /v1/yields/{id}/validators` | + +### Bittensor (`bittensor-native-staking`) + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string in TAO | +| `validatorAddress` | Yes | Get from `GET /v1/yields/{id}/validators` (validator objects include a `subnet{}` block) | +| `subnetId` | Yes | **Number** — the subnet identifier for Bittensor staking | + +All chain-specific arguments are flat keys inside `arguments` (no `additionalAddresses` wrapper). Confirm exact required fields via `mechanics.arguments.enter.fields[]` in the yield DTO. + +## Signing + +```typescript +import { ApiPromise, WsProvider } from "@polkadot/api"; +import { Keyring } from "@polkadot/keyring"; + +const provider = new WsProvider("wss://rpc.polkadot.io"); +const api = await ApiPromise.create({ provider }); +const keyring = new Keyring({ type: "sr25519" }); +const account = keyring.addFromUri("//Alice"); + +for (const tx of action.transactions) { + const payload = JSON.parse(tx.unsignedTransaction); + + // Reconstruct the extrinsic and sign the full payload (era, nonce, blockHash, ...), + // not just the method bytes — otherwise the signature is invalid. + const extrinsic = api.createType("Extrinsic", payload.method); + const signingPayload = api.createType("ExtrinsicPayload", payload, { + version: extrinsic.version, + }); + const { signature } = signingPayload.sign(account); + extrinsic.addSignature(account.address, signature, signingPayload.toHex()); + + // Broadcast + const hash = await api.rpc.author.submitExtrinsic(extrinsic); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash: hash.toHex() }); +} +``` + +## Common Gotchas + +1. **Nomination pools vs direct staking**: Polkadot supports both. The API handles this via different yieldIds. + +2. **28-day unbonding**: Polkadot has a 28-day unbonding period for native staking. + +3. **Minimum stake**: Polkadot requires a minimum of 250 DOT for direct nomination. For smaller amounts, use nomination pools. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=polkadot" \ + -H "x-api-key: YOUR_KEY" +curl "https://api.yield.xyz/v1/yields?network=bittensor" \ + -H "x-api-key: YOUR_KEY" +``` + +Common yieldIds: +- `polkadot-dot-validator-staking` +- `bittensor-native-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md new file mode 100644 index 0000000..bcb4f8a --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md @@ -0,0 +1,56 @@ +# Sui Integration Guide + +## unsignedTransaction Format + +**Encoding:** Base64-encoded BCS transaction bytes +**Parse before signing:** No — base64 decode and pass to Sui SDK + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"10"` = 10 SUI | +| `validatorAddress` | Yes (native staking) | Get from `GET /v1/yields/{id}/validators` | + +## Signing + +```typescript +import { SuiClient, getFullnodeUrl } from "@mysten/sui/client"; +import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; +import { Transaction } from "@mysten/sui/transactions"; + +const client = new SuiClient({ url: getFullnodeUrl("mainnet") }); +const keypair = Ed25519Keypair.fromSecretKey(privateKey); + +for (const tx of action.transactions) { + const txBytes = Buffer.from(tx.unsignedTransaction, "base64"); + + // Sign + const signature = await keypair.signTransaction(txBytes); + + // Execute + const result = await client.executeTransactionBlock({ + transactionBlock: tx.unsignedTransaction, + signature: signature.signature, + }); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash: result.digest }); +} +``` + +## Common Gotchas + +1. **Object model**: Sui uses an object-centric model. The API handles object references internally. + +2. **Gas objects**: Sui requires explicit gas objects. The API includes gas configuration in the transaction. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=sui" \ + -H "x-api-key: YOUR_KEY" +``` + +Common yieldIds: +- `sui-sui-native-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md new file mode 100644 index 0000000..4f280ef --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md @@ -0,0 +1,56 @@ +# Tezos Integration Guide + +## unsignedTransaction Format + +**Encoding:** Hex-encoded forged operation bytes +**Parse before signing:** No — hex decode and pass to Tezos signing SDK + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"100"` = 100 XTZ | +| `tezosPubKey` | Yes | Tezos public key. Flat top-level field inside `arguments` (no `additionalAddresses` wrapper) | + +Confirm exact required fields via `mechanics.arguments.enter.fields[]` in the yield DTO. + +## Signing + +```typescript +import { TezosToolkit } from "@taquito/taquito"; +import { InMemorySigner } from "@taquito/signer"; + +const tezos = new TezosToolkit("https://mainnet.api.tez.ie"); +tezos.setProvider({ signer: new InMemorySigner(privateKey) }); + +for (const tx of action.transactions) { + const forgedBytes = tx.unsignedTransaction; // hex string + + // Sign the forged bytes + const signed = await tezos.signer.sign(forgedBytes); + + // Broadcast + const hash = await tezos.rpc.injectOperation(signed.sbytes); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash }); +} +``` + +## Common Gotchas + +1. **tezosPubKey required**: Always include the flat `tezosPubKey` field (inside `arguments`, not under any `additionalAddresses` wrapper) or you'll get a 422 error. + +2. **Delegation vs staking**: Tezos uses "delegation" (baking) rather than direct staking. Delegated funds remain liquid. + +3. **No lock period**: Tezos delegation has no unbonding period — you can change bakers or undelegate at any time. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=tezos" \ + -H "x-api-key: YOUR_KEY" +``` + +Common yieldIds: +- `tezos-xtz-native-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md new file mode 100644 index 0000000..3c1e944 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md @@ -0,0 +1,66 @@ +# TON Integration Guide + +## unsignedTransaction Format + +**Encoding:** JSON string with BOC (Bag of Cells) data +**Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` + +The API returns a JSON string containing the transaction message in BOC format. + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"10"` = 10 TON | + +For `ton-ton-tston-staking`, `requiresValidatorSelection=false` and enter requires only `amount`. The other pools (`ton-ton-P2P-pools-staking`, `ton-ton-ton-whales-pools-staking`, `ton-ton-chorus-one-pools-staking`) have `requiresValidatorSelection=true` and additionally require `validatorAddress`. Confirm exact required fields via `mechanics.arguments.enter.fields[]` in the yield DTO. + +## Signing + +```typescript +import { TonClient, WalletContractV4 } from "@ton/ton"; +import { mnemonicToPrivateKey } from "@ton/crypto"; + +const client = new TonClient({ endpoint: "https://toncenter.com/api/v2/jsonRPC" }); +const keyPair = await mnemonicToPrivateKey(mnemonic.split(" ")); +const wallet = WalletContractV4.create({ publicKey: keyPair.publicKey, workchain: 0 }); + +for (const tx of action.transactions) { + const txData = JSON.parse(tx.unsignedTransaction); + + // Create and sign the transfer + const contract = client.open(wallet); + const seqno = await contract.getSeqno(); + + await contract.sendTransfer({ + seqno, + secretKey: keyPair.secretKey, + messages: [/* construct from txData */], + }); + + // Submit hash — MANDATORY + // TON uses a different hash format — get it from the transaction result + await sdk.api.submitTransactionHash(tx.id, { hash: txHash }); +} +``` + +## Common Gotchas + +1. **BOC format**: TON uses a unique Bag of Cells serialization. Make sure you're using the `@ton/ton` library for correct parsing. + +2. **Seqno**: TON wallets use sequence numbers. Each transaction must use the next seqno. + +3. **Workchain**: Most user wallets are on workchain 0. Ensure your signing matches. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=ton" \ + -H "x-api-key: YOUR_KEY" +``` + +Common yieldIds (all are staking pools): +- `ton-ton-tston-staking` (`requiresValidatorSelection=false`, enter requires only `amount`) +- `ton-ton-P2P-pools-staking` +- `ton-ton-ton-whales-pools-staking` +- `ton-ton-chorus-one-pools-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md new file mode 100644 index 0000000..f717315 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md @@ -0,0 +1,58 @@ +# Tron Integration Guide + +## unsignedTransaction Format + +**Encoding:** JSON string with transaction object +**Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` + +## Required Arguments + +| Argument | Required | Description | +|----------|----------|-------------| +| `amount` | Yes | Human-readable string. `"100"` = 100 TRX | +| `validatorAddresses` | Yes (native staking) | **Array (plural)**. Get from `GET /v1/yields/{id}/validators` | +| `tronResource` | Yes | Enum, one of `["ENERGY", "BANDWIDTH"]` — specifies the resource type | + +All arguments are flat keys inside `arguments`. Confirm exact required fields via `mechanics.arguments.enter.fields[]` in the yield DTO. + +## Signing + +```typescript +const TronWeb = require("tronweb"); + +const tronWeb = new TronWeb({ + fullHost: "https://api.trongrid.io", + privateKey: PRIVATE_KEY, +}); + +for (const tx of action.transactions) { + const txData = JSON.parse(tx.unsignedTransaction); + + // Sign + const signedTx = await tronWeb.trx.sign(txData); + + // Broadcast + const result = await tronWeb.trx.sendRawTransaction(signedTx); + + // Submit hash — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash: result.txid }); +} +``` + +## Common Gotchas + +1. **tronResource required**: Always include `tronResource: "BANDWIDTH"` or `"ENERGY"`. Missing this causes a 422 error. + +2. **Resource model**: Tron uses BANDWIDTH and ENERGY resources instead of gas. Staking TRX gives you these resources. + +3. **3-day unstaking**: Tron native staking has a 3-day unstaking period. + +## Available Yields + +```bash +curl "https://api.yield.xyz/v1/yields?network=tron" \ + -H "x-api-key: YOUR_KEY" +``` + +Common yieldIds: +- `tron-trx-native-staking` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md index 31d63d4..07705b1 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md @@ -193,19 +193,35 @@ confirmation of each transaction before starting the next. ## 13. Assuming an HTTP Status Is From `api.yield.xyz` When It Isn't -**Error:** Debugging an unexpected HTTP status (e.g. `412 Precondition Failed`, -`502`, `520`, `530`) as if it came from the Yield.xyz API, when the code isn't -documented for the endpoint you called. +**Error:** Debugging an unexpected HTTP status (e.g. `502`, `504`, `520`, `530`) +as if it came from the Yield.xyz API, when it was actually injected by an edge +layer between your app and our API. **What happens:** You waste time looking for a Yield.xyz-side cause that doesn't exist. The status almost certainly came from something *between* your app and our API — a CDN, reverse proxy, HTTP middleware, or service worker — not from `api.yield.xyz`. -Documented Yield.xyz error codes per endpoint: `400`, `401`, `403`, `404`, `422`, -`429`, `500`. Anything else is suspect. `412` in particular is never returned by -any Yield.xyz endpoint — it's commonly injected by edge layers for -`If-Match` / `If-Unmodified-Since` preconditions. +Yield.xyz application error codes: `400`, `401`, `403`, `404`, `412`, `422`, `429`, +`500`. **`412` IS a real Yield.xyz response** — it means a precondition failed (the +action is blocked *right now*: yield closed for deposits/withdrawals per +`status.enter`/`status.exit`, a blocked yield, or resubmitting a different hash to a +terminal transaction), **not** a malformed request and **not** an edge artifact. Codes +that are NOT ours and signal an intermediary: `502`, `504`, `520`/`521`/`530` +(Cloudflare), or any HTML response body. + +The real Yield.xyz error envelope is: + +```json +{ "statusCode": 400, "timestamp": "…", "path": "/v1/…", "message": "…", "validation": { "message": ["…"] }, "details": { "error": "…" } } +``` + +There is **no top-level `error` field.** `validation` and `details` are optional. +**Validation failures return `400`** (not `422`) with a `validation.message[]` array +of human-readable messages. A bad or disabled `yieldId` returns **`400`** with +`message: "Yield \"…\" is not enabled for this project"` — **not `404`.** (`YieldErrorDto` +`{ yieldId, error }` is a different thing: a per-yield partial-failure embed that appears +*inside a successful list response*, not the HTTP error envelope.) **Fix:** Before treating an unusual status as an API bug: @@ -213,12 +229,14 @@ any Yield.xyz endpoint — it's commonly injected by edge layers for or `yield_troubleshoot_error({ error, context })` — the troubleshoot tool will confirm whether the code is documented for that endpoint. 2. **Inspect the raw response body.** If it's HTML or not our standard JSON - error shape `{ message, error, statusCode }`, the response is from an - intermediary, not us. + error shape `{ statusCode, timestamp, path, message, validation?, details? }`, + the response is from an intermediary, not us. 3. **Check your own stack:** HTTP client middleware, CDN (Cloudflare, Fastly), reverse proxy (nginx, AWS ALB), or service worker rewriting responses. -4. **Double-check the actual status code.** `422` can be misread as `412` in - logs — verify from the network tab or a fresh `curl`. +4. **Double-check the actual status code and what it means.** `400` (bad request), + `412` (action blocked — check `status.enter`/`status.exit`), and `422` + (unprocessable) are distinct, real codes — don't conflate them; verify from the + network tab or a fresh `curl`. Only contact `hello@yield.xyz` once you've confirmed the response is from `api.yield.xyz` directly with our JSON error shape. @@ -301,3 +319,32 @@ import { MiscChainIds, SKApp } from '@stakekit/widget'; the widget defaults to ETH/Lido even when `initialChain` is set to a non-EVM network. Always pass both. For ERC-20 / SPL tokens with a real contract address, use `-` as the key instead. + +--- + +## 16. Wrong Shape for Chain-Specific Action Arguments + +**Error:** Nesting chain-specific arguments under an `additionalAddresses` wrapper +(e.g. `arguments.additionalAddresses.cosmosPubKey`), or guessing array vs. scalar +for pool arguments. + +**What happens:** `400 Bad Request` with a `validation.message[]` like +`["cosmosPubKey should not be empty"]` — the API never sees the value because it's +in the wrong place. + +**Fix:** Chain-specific arguments are **flat keys directly inside `arguments`** — there +is no `additionalAddresses` wrapper: + +- Cosmos: `arguments.cosmosPubKey` +- Tezos: `arguments.tezosPubKey` +- Tron: `arguments.validatorAddresses` (a plural **array**) + `arguments.tronResource` +- Bittensor: `arguments.subnetId` + +Pool argument shapes also trip people up: + +- `liquidity_pool` **enter** uses `arguments.amounts` (an **array**, one per pool token) +- `concentrated_liquidity_pool` **exit** needs `arguments.percentage` + `arguments.tokenId` + (there is **no `amount`** on this exit) + +Always read the yield's `mechanics.arguments.enter` / `.exit` schema — each field's +`name`, `isArray`, and `required` are the contract. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md new file mode 100644 index 0000000..93a7d5d --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md @@ -0,0 +1,38 @@ +# Dashboard & API Keys — What's Configured Outside the API + +A large part of how the Yield.xyz API behaves for a given integration is **not** controlled in code — it's controlled in the **dashboard** (https://dashboard.yield.xyz) and attached to the **API key**. A builder who doesn't understand this will hit confusing errors that look like API bugs but are really configuration. + +## The mental model + +- Your **API key belongs to a project.** The project's settings in the dashboard decide what that key can see and do. +- The dashboard controls, per project/key: + - **Which yields are enabled** — you turn individual yields (and whole networks/providers) on or off. + - **Fees** — deposit, performance, and management fees are configured here, not passed in code. They're applied server-side to the actions the API builds for your key. + - **Which products/features are enabled** — Yield, Perps, Borrow, and specific capabilities can be toggled per key. + - **Rate-limit tier** and other account-level limits. + +## The consequence builders must internalize + +**`GET /v1/yields` returns the global catalog — being in the catalog does NOT mean it's enabled for your key.** + +So: + +- A yield can appear in `GET /v1/yields` (or `yields_get_all`) yet **not be usable by your key**. Calling `GET /v1/yields/{yieldId}`, `actions/enter`, etc. on a yield that isn't enabled for your project returns: + + ```json + { "statusCode": 400, "message": "Yield \"\" is not enabled for this project", "details": { "error": "Bad Request" } } + ``` + + This is **HTTP 400, not 404** — it's a configuration state, not "doesn't exist." (An unknown id returns the same 400 shape.) + +- Don't treat "in the catalog" as "available." If you're building a picker, either (a) enable exactly the yields you need in the dashboard and rely on that, or (b) verify availability by fetching `GET /v1/yields/{id}` and handling the 400 gracefully. + +- **Fees you see in the built transaction come from dashboard config**, not from anything you sent. To change them, change the dashboard configuration — there's no code path for it in the action request. + +- If an endpoint or product returns 401/403 unexpectedly, check the **key's enabled features** in the dashboard before assuming an API bug. + +## What this means for generated code + +- When a user reports "this yield 400s," first ask: **is it enabled for their key in the dashboard?** That's the most common cause. +- Generated onboarding/setup instructions should tell the user to enable the yields/networks they need at https://dashboard.yield.xyz. +- Never hardcode the assumption that a catalog yield is enterable — gate on a successful `GET /v1/yields/{id}`. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md index d533ee2..54fe4d5 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md @@ -61,7 +61,29 @@ Mobile/Web App -> @yieldxyz/sdk -> Yield.xyz API The `@stakekit/widget` is a pre-built React component that handles the entire yield flow — discovery, entry, exit, and management. No need to call the API directly. -Refer to the widget SDK docs for installation and configuration. +```tsx +import "@stakekit/widget/package/css"; +import { SKApp, darkTheme } from "@stakekit/widget"; + +function YieldPage() { + return ; +} +``` + +The React component is `SKApp` (requires React 19+). For non-React apps use the bundled +build (`renderSKWidget` from `@stakekit/widget/bundle`). Refer to the widget docs and the +[widget repo](https://github.com/stakekit/widget) for installation and configuration. + +### SDK Integration (More Control) +```typescript +const yields = await sdk.api.getYields({ network: "ethereum", token: "ETH" }); +const action = await sdk.api.enterYield({ + yieldId: "ethereum-eth-lido-staking", + address: userAddress, + arguments: { amount: "1.0" }, +}); +// Present transactions to user for signing +``` --- @@ -86,6 +108,16 @@ Your App -> Your Backend -> Yield.xyz API (per-tenant API keys) - **Reporting:** Use `POST /v1/yields/balances` to generate portfolio reports - **Common pitfalls:** See `common-pitfalls.md` — especially entries #1, #2, #6 +### Fee Setup +``` +POST /v1/programmatic/projects/{id}/fee-config +{ + "depositFeePercent": 0.5, + "performanceFeePercent": 15, + "managementFeePercent": 2 +} +``` + --- ## Yield Aggregator @@ -103,7 +135,7 @@ Your Frontend -> Your Backend -> Yield.xyz API ### Key Considerations - **Yield discovery:** Use `GET /v1/yields` with filters to find all available opportunities. Pass `network`, `token`, `type`, `provider` as API query params — never fetch-all and filter client-side. -- **APY comparison:** Pass `sort` as an API query param (e.g. `?sort=apy:desc` — check the live spec via `yield_get_api_spec({ endpoint: "/v1/yields" })` for the exact sort syntax and supported fields). **Do NOT** fetch the paginated response and then `.sort()` on the client — that produces wrong results across pages and defeats pagination. Client-side sort is only acceptable if the live spec shows no `sort` param exists, which is rare. +- **APY comparison:** Pass `sort` as an API query param (e.g. `?sort=rewardRateDesc` — the `YieldSortingOption` enum value for APY descending; there is no `apy` sort key. Check the live spec via `yield_get_api_spec({ endpoint: "/v1/yields" })` for the full set of supported sort values). **Do NOT** fetch the paginated response and then `.sort()` on the client — that produces wrong results across pages and defeats pagination. Client-side sort is only acceptable if the live spec shows no `sort` param exists, which is rare. - **Text search:** If users can type a protocol / token name, pass `search=` (or whatever the live spec calls it) through to the API rather than a client-side `.toLowerCase().includes(...)`. - **Risk scoring:** Factor in protocol risk, TVL, and audit status from the yield response - **Auto-rebalancing:** Periodically check APYs and move funds to higher-yielding opportunities @@ -111,6 +143,32 @@ Your Frontend -> Your Backend -> Yield.xyz API --- +## RWA / KYC Builder Flow + +Tokenized real-world-asset yields are often **KYC/accreditation gated** — the issuer +requires the wallet to be verified before it can enter the position. Build the gate +into the flow; don't let the user reach a signing step they'll be rejected at. + +### Flow +1. **Discover RWA yields:** `GET /v1/yields?type=real_world_asset` (snake_case — see `yield-types.md`). +2. **Check KYC status for the user's wallet** on a gated yield: + `GET /v1/yields/{yieldId}/kyc/status?address=0x...` +3. **Read the `KycStatusResponseDto`** — `{ kycStatus, authorizeUrl? }`, where + `kycStatus` is one of: `not_required | not_started | pending | approved | rejected`. +4. **Gate the Enter action:** only allow entry once `kycStatus === "approved"`. + For any other status, redirect the user to `authorizeUrl` (the issuer's KYC portal) + to complete verification instead of presenting an Enter button. + +### Example (verified live) +``` +GET /v1/yields/ethereum-usdc-securitize-acred-vault/kyc/status?address=0x... +-> { "kycStatus": "not_started", "authorizeUrl": "https://id.securitize.io/" } +``` +Here `kycStatus !== "approved"`, so gate Enter and send the user to +`https://id.securitize.io/` to complete KYC. + +--- + ## Enterprise Backend **Approach:** REST API with infrastructure-grade patterns @@ -125,6 +183,32 @@ Enterprise integrations need reliability, monitoring, and compliance controls. - **Multi-region:** Use geographically distributed API calls for resilience - **Timeout:** Set a 3-second max timeout on all API calls to avoid hanging requests +### Infrastructure Pattern +```typescript +class YieldService { + private sdk: Sdk; + private metrics: MetricsCollector; + private guardrails: GuardrailConfig; + + async enterYield(params: EnterParams) { + this.metrics.increment("yield.enter.attempt"); + + // Pre-checks + await this.validateGuardrails(params); + await this.validateBalance(params); + + const action = await this.sdk.api.enterYield(params); + + for (const tx of action.transactions) { + await this.validateWithShield(tx); + const hash = await this.signAndBroadcast(tx); + await this.sdk.api.submitTransactionHash(tx.id, { hash }); + this.metrics.increment("yield.enter.success"); + } + } +} +``` + --- ## Mobile App diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md index 70eb4ed..7f86516 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md @@ -10,26 +10,19 @@ Consult this file whenever you're unsure if a given MCP tool is safe to invoke d | Category | Use during builder sessions? | Why | |---|---|---| -| **Doc tools** (`yield_*` prefixed with `get_`, `lookup`, `fetch`, `list`, `troubleshoot`) | ✅ Yes — freely | Read-only references pulled from live docs / OpenAPI spec. Authoritative and always current. | +| **Doc tools** (`yield_get_api_spec`, `yield_lookup_docs`, `yield_fetch_doc`, `yield_troubleshoot_error`, `yield_list_repos`) | ✅ Yes — freely | Read-only, and all **live**: they pull the current OpenAPI spec, search/read the live docs, diagnose errors against the live spec, or point at real source repos. Authoritative and always current. | +| **Static guidance** (this skill's `references/*.md`) | ✅ Yes — read directly | Chains, transaction lifecycle, yield types, safety, limits, integration patterns. Curated and bundled with the skill — no tool call needed. | | **Action tools** (`yields_get*`, `actions_*`) | ❌ **No — never** | Return trimmed/slimmed responses that omit fields present in the full REST API. Code built from their responses will be wrong. Use `curl`/`fetch` against `https://api.yield.xyz` with the user's API key instead. | The **one purpose** of action tools is to let an end-user execute yield flows via an AI agent — not to let a builder inspect schemas. For building, always go through the live REST API. +> **Note:** The MCP used to also expose ~10 static doc tools (`yield_get_overview`, `yield_get_chain_guide`, `yield_get_transaction_guide`, `yield_get_yields_endpoint_guide`, `yield_get_safety_rules`, `yield_get_api_limits`, `yield_get_tool_reference`, `yield_list_doc_topics`, `yield_recommend_stack`, `yield_get_integration_guide`). Those have been removed — their content now lives in this skill's `references/` files. If you see them referenced anywhere, read the corresponding skill file instead (mapping below). + --- ## ✅ Doc Tools — Use These -All doc tools are read-only and safe. Prefer them over web searches or guessing. - -### `yield_get_overview` -- Full tool index and routing guide for the Yield.xyz MCP. Covers action tools vs. doc tools, verification rules, and common flows. - -- **Use when:** "where do I start", "which tool", "overview", "how does this work", "getting started". - -### `yield_list_doc_topics` -- Structured map of all Yield.xyz documentation, organized by category. Accepts optional `category` filter. - -- **Use when:** "what docs exist", "show me all topics", "what can I read about", "documentation overview". +The MCP exposes **five** live doc tools, all read-only and safe. Prefer them over web searches or guessing. (Static "how-to-build" guidance — chains, transaction lifecycle, yield types, safety, limits — is **not** on the MCP; it lives in this skill's own `references/` files, listed under [Static Guidance](#static-guidance--read-from-this-skills-reference-files) below.) ### `yield_lookup_docs` - Full-text search across 209 Yield.xyz documentation pages. Returns ranked results (title, excerpt, `.md` URL). Follow up with `yield_fetch_doc` to read the full page. @@ -54,37 +47,7 @@ The `product` arg selects which spec to fetch: **Use when:** Before generating any integration code, for any of the three products. Always verify current field names against this tool — don't trust memory. -### `yield_get_api_limits` -- Rate limits, API key tiers, throttling behavior, retry guidance. - -- **Use when:** "rate limits", "429", "how many requests", "production key", "throttling". - -### `yield_get_chain_guide` -- How to handle Yield.xyz transactions on a specific blockchain. Covers `unsignedTransaction` format, encoding, parsing, which signing SDK to use, required chain-specific arguments (e.g. `cosmosPubKey`, `tezosPubKey`, `tronResource`), common gotchas, and example API flow. Resolves chain family live from `GET /v1/networks` — supports all 90+ networks. - -- **Use when:** "how do transactions work on Cosmos/Solana/Base", "what format is unsignedTransaction on [chain]", "what signing SDK for [chain]". - -### `yield_get_safety_rules` -- Safety guardrails and pre-execution checks. Risk levels, 6 pre-execution checks, 7 safety rules, configurable guardrails. - -- **Use when:** "safety checks", "guardrails", "pre-action checklist", "risk controls". - -### `yield_get_tool_reference` -- Reference for the Yield.xyz action tools — inputs, outputs, example prompts, transaction format, and execution rules. (The MCP exposes **17 action tools** in total; the full list with one-line descriptions and REST equivalents is in the [Action Tools](#-action-tools--do-not-call-during-builder-sessions) section below.) - -- **Use when:** Explaining to the user what the MCP action tools do (e.g. "what tools are available", "how does actions_enter work"). **Do not use as justification to actually call those action tools in a builder session.** - -### `yield_get_transaction_guide` -- Step-by-step guide through the entire transaction flow: discover → read schema → call action → handle unsigned transactions → sign → broadcast → submit hash → confirm. Includes server-side and browser wallet (MetaMask, Phantom EVM) signing examples. Accepts optional `chain` parameter for chain-specific examples. - -- **Use when:** "how do transactions work", "full flow", "end to end", "what's stepIndex", "submit hash", "how to sign", "transaction lifecycle", "MetaMask", "browser wallet", "waitForTransaction", "invalid params". - -### `yield_get_yields_endpoint_guide` -- Complete reference for `GET /v1/yields` — the single most-used Yield.xyz endpoint. Covers every query param (filters: `network`/`networks`, `token`/`inputToken`/`inputTokens`, `provider`/`providers`, `type`/`types`, `yieldId`/`yieldIds` up to 100, `chainId`, `hasCooldownPeriod`, `hasWarmupPeriod`), **server-side `search`**, **server-side `sort`** (`YieldSortingOption` enum — `rewardRateDesc` = APY desc), pagination (`limit`/`offset`), response shape, and one-call query patterns that replace client-side fan-out. Also covers all 8 `YieldType` values (staking, liquid-staking, restaking, lending, vault, liquidity-pool, concentrated-liquidity-pool, real-world-asset) with mechanics, lock periods, required arguments, typical APY. Accepts optional `yieldType` filter for a single type's section. - -- **Use when:** "how do I filter yields", "how do I sort by APY", "can I query multiple chains at once", "what params does /v1/yields take", "server-side sort", "search yields", "yields_get_all options", "staking vs lending", "lock periods", "what yield types exist", "which type needs validators", "receipt tokens", "concentrated liquidity vs regular liquidity pool", "real-world asset yields". - -- **Backwards-compat:** the old tool name `yield_get_yield_types_guide` is kept as an alias — both names work, same content. +> **Chain formats, transaction lifecycle, yield types, safety rules, and API limits used to be MCP doc tools (`yield_get_chain_guide`, `yield_get_transaction_guide`, `yield_get_yields_endpoint_guide`, `yield_get_safety_rules`, `yield_get_api_limits`).** They are no longer on the MCP — that guidance now lives in this skill's `references/` files. See [Static Guidance](#static-guidance--read-from-this-skills-reference-files). ### `yield_troubleshoot_error` - Diagnoses API errors, HTTP status codes, or unexpected responses. For unrecognized errors, automatically looks up the live OpenAPI spec for the authoritative error shape. @@ -162,24 +125,39 @@ MCP response may not. When you need information during a builder session: +**Live needs → MCP doc tools:** ``` Need API reference / schema? → yield_get_api_spec (pass product: "perps" or "borrow" when integrating those products; default product is "yield") +Need other endpoint schemas? → yield_get_api_spec({ endpoint: "..." }) Need a working code example / stuck implementing something? → yield_list_repos, then read the raw source -Need sort/search/filter params for - /v1/yields specifically? → yield_get_yields_endpoint_guide — covers every - filter, multi-value syntax, sort enum, and - one-call patterns (replaces client-side fan-out) -Need other endpoint schemas? → yield_get_api_spec({ endpoint: "..." }) -Need transaction / signing guide? → yield_get_transaction_guide or yield_get_chain_guide -Need yield-type mechanics? → yield_get_yields_endpoint_guide (Part 2) -Need safety / limits / policies? → yield_get_safety_rules / yield_get_api_limits -Need to explore docs broadly? → yield_list_doc_topics → yield_lookup_docs → yield_fetch_doc +Need to explore docs broadly? → yield_lookup_docs → yield_fetch_doc Hit an error? → yield_troubleshoot_error +``` + +**Static "how-to-build" needs → read this skill's `references/` files (no tool call):** +``` +Sort/search/filter params for /v1/yields, + or yield-type mechanics? → references/yield-types.md +Transaction lifecycle / signing / + stepIndex / submit-hash? → references/transaction-lifecycle.md +unsignedTransaction format & signing + SDK for a specific chain? → references/signing-patterns.md + → references/chains/.md +Safety rules / pre-execution checks / + guardrails / policies? → references/policies.md +Rate limits / 429 / key tiers? → references/api-limits.md +Which integration approach (widget / + SDK / REST / programmatic)? → references/setup.md +Integration architecture patterns? → references/integration-patterns.md +Field naming / common mistakes? → references/api-field-mapping.md, references/common-pitfalls.md +``` +**Data / execution:** +``` Need real yield / balance / action data? → DO NOT call MCP action tools. → Use the user's API key to call the REST endpoint at @@ -192,6 +170,24 @@ User wants to actually run/test an --- +## Static Guidance — read from this skill's reference files + +These topics are **not** MCP tools. Read the file directly when you need the guidance. + +| Need | Read | (Former MCP tool) | +|---|---|---| +| `/v1/yields` query params + the 8 yield types & mechanics | `references/yield-types.md` | `yield_get_yields_endpoint_guide` | +| End-to-end transaction flow (discover → sign → submit-hash → confirm) | `references/transaction-lifecycle.md` | `yield_get_transaction_guide` | +| Per-chain `unsignedTransaction` format & signing SDK | `references/signing-patterns.md` + `references/chains/.md` | `yield_get_chain_guide` | +| Safety rules, pre-execution checks, guardrails | `references/policies.md` | `yield_get_safety_rules` | +| Rate limits, key tiers, throttling | `references/api-limits.md` | `yield_get_api_limits` | +| Choosing an integration approach (widget / SDK / REST / programmatic) | `references/setup.md` | `yield_recommend_stack` | +| Integration architecture patterns by product type | `references/integration-patterns.md` | `yield_get_integration_guide` | + +The action-tool table above replaces the former `yield_get_tool_reference` and `yield_get_overview` tools. + +--- + ## Why This Matters The builder skill's job is to produce **production-ready code**. That requires accurate schemas. MCP action tools are tuned for token efficiency in agent chats, not for API fidelity — so they are the wrong reference surface for code generation. Sticking to doc tools + live REST calls is what prevents the skill from hallucinating field names or generating calls that silently drop required data. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md index ef821ec..8aefa40 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md @@ -24,8 +24,13 @@ When generating code that displays yield data, use these formats: ## Yield Listing When generating a yield discovery UI, display results in a table sorted by APY descending. +Use the API sort parameter `sort=rewardRateDesc` (there is no `sort=apy:desc`). -Recommended columns: Protocol, Name/Vault, APY, TVL, Type, Network, Status. +There is **no top-level `apy` field** on a yield. APY lives at `rewardRate.total`, and +`rewardRate.rateType` tells you the rate kind (e.g. `"APY"`). Read APY from `rewardRate.total` +(and check `rewardRate.rateType === "APY"`), not a top-level `apy`. + +Recommended columns: Protocol, Name/Vault, APY (`rewardRate.total`), TVL, Type, Network, Status. ### TVL Filtering @@ -54,8 +59,8 @@ between each. ## Reward Rate Breakdown -When showing a single yield's APY, expand the components array to show where the -yield comes from (base lending rate, incentive rewards, etc.). +When showing a single yield's APY (`rewardRate.total`), expand the `rewardRate.components` +array to show where the yield comes from (base lending rate, incentive rewards, etc.). Flag any incentive/bonus component as potentially temporary. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md index 21021d1..4702863 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md @@ -27,8 +27,9 @@ Guidelines for efficient API usage in Yield.xyz integrations. ## Pagination - Default `limit`: 20 -- Maximum `limit`: 50 -- Use `offset` for pagination +- Maximum `limit`: **100** — a `limit` greater than 100 returns **HTTP 400** +- Use `offset` for pagination — pagination is **offset-only**, there is no cursor +- The response envelope is `{ items, total, offset, limit }` — the array key is **`items`** (not `data`) - Do not attempt to fetch all yields in a single request --- @@ -42,3 +43,95 @@ Guidelines for efficient API usage in Yield.xyz integrations. 5. **Log all submit-hash calls.** If a hash submission fails, positions won't update — you need to retry 6. **Handle 503 gracefully.** Upstream protocols can be temporarily unavailable 7. **Set a 3-second timeout** on all API calls to avoid hanging requests + +--- + +## Safety rules & pre-execution checks + +### Risk Levels + +Every yield opportunity in the Yield.xyz API has an associated risk profile. Note there is +**no top-level `risk` field on the yield object** — fetch the risk profile from +`GET /v1/yields/{id}/risk`. Before executing any action, evaluate the risk: + +| Risk Level | Description | Example | +|------------|-------------|---------| +| Low | Blue-chip protocols, audited, large TVL | Lido ETH staking, Aave USDC lending | +| Medium | Established protocols, moderate TVL | Smaller liquid staking providers | +| High | Newer protocols, smaller TVL, complex strategies | New vault strategies, leveraged positions | + +### 6 Pre-Execution Checks + +Before calling any action endpoint (`POST /v1/actions/enter`, `exit`, `manage`): + +1. **Yield exists and is active**: Call `GET /v1/yields/{id}` and verify `status.enter` is `true` +2. **Amount within limits**: Check `entryLimits.minimum` and `entryLimits.maximum` in yield metadata +3. **User has sufficient balance**: Verify the user's token balance covers the amount plus gas +4. **Read the schema**: Always read `mechanics.arguments.enter` (or `.exit`) to know required fields +5. **Validator is valid** (staking only): Use `GET /v1/yields/{id}/validators` — never hardcode +6. **Chain-specific args present**: Cosmos needs `cosmosPubKey`, Tron needs `tronResource`, etc. + +### 7 Safety Rules + +1. **Never modify `unsignedTransaction`** — sign exactly as returned by the API. Any modification will cause the transaction to fail or behave unexpectedly. +2. **Execute in `stepIndex` order** — multi-step transactions (e.g., EVM approve + deposit) must be executed sequentially. Wait for `CONFIRMED` status before proceeding. +3. **Always submit hash** — after broadcasting, call `PUT /v1/transactions/{txId}/submit-hash`. Without this, balances won't update. +4. **Amounts are human-readable** — pass `"100"` for 100 USDC, not `"100000000"`. The API handles decimal conversion. +5. **Use Shield for validation** — before signing, validate with `@yieldxyz/shield`: `shield.validate({ unsignedTransaction, yieldId, userAddress })`. +6. **Handle pending actions** — after entering a position, check balances for `pendingActions`. These are follow-up transactions the user must complete (e.g., claiming rewards). +7. **Respect cooldown periods** — some yields have unbonding/cooldown periods. After `exit`, the balance moves to `"exiting"` status before becoming `"withdrawable"`. + +### Configurable Guardrails + +You can implement additional guardrails in your application: + +```json +{ + "maxSingleTransactionUsd": 10000, + "allowedRiskLevels": ["low", "medium"], + "requireShieldValidation": true, + "allowedNetworks": ["ethereum", "base", "arbitrum"], + "requireUserConfirmation": true, + "maxDailyVolumeUsd": 50000 +} +``` + +#### Implementation Pattern + +```typescript +async function executeWithGuardrails(params: { + yieldId: string; + amount: string; + address: string; + guardrails: GuardrailConfig; +}) { + const yield_ = await sdk.api.getYield(params.yieldId); + + // Risk is NOT a field on the yield object — fetch it from GET /v1/yields/{id}/risk + const risk = await sdk.api.getYieldRisk(params.yieldId); + if (!params.guardrails.allowedRiskLevels.includes(risk.level)) { + throw new Error(`Risk level ${risk.level} not allowed`); + } + + if (!params.guardrails.allowedNetworks.includes(yield_.network)) { + throw new Error(`Network ${yield_.network} not allowed`); + } + + const usdValue = parseFloat(params.amount) * yield_.token.price; + if (usdValue > params.guardrails.maxSingleTransactionUsd) { + throw new Error(`Transaction value $${usdValue} exceeds limit`); + } + + if (params.guardrails.requireShieldValidation) { + const action = await sdk.api.enterYield({ ... }); + for (const tx of action.transactions) { + const valid = await shield.validate({ + unsignedTransaction: tx.unsignedTransaction, + yieldId: params.yieldId, + userAddress: params.address, + }); + if (!valid) throw new Error("Shield validation failed"); + } + } +} +``` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md index dde584e..3c2a796 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md @@ -10,12 +10,13 @@ ## Step 1 — Register the Yield.xyz MCP Server (REQUIRED — do this first) -**This step is mandatory, not optional.** The builder skill depends on the doc tools -exposed by the `yield-agentkit` MCP server (`yield_get_api_spec`, -`yield_get_chain_guide`, `yield_get_transaction_guide`, `yield_troubleshoot_error`, -etc.). Without the MCP registered, the skill cannot fetch the live OpenAPI spec, -look up chain-specific signing guidance, or diagnose API errors — so code generation -will fall back on stale/hallucinated information. +**This step is mandatory, not optional.** The builder skill depends on the live tools +exposed by the `yield-agentkit` MCP server (`yield_get_api_spec`, `yield_lookup_docs`, +`yield_fetch_doc`, `yield_troubleshoot_error`, `yield_list_repos`). Without the MCP +registered, the skill cannot fetch the live OpenAPI spec, search/read the live docs, or +diagnose API errors against the live spec — so code generation will fall back on +stale/hallucinated information. (Chain-specific signing, transaction lifecycle, yield +types, and safety guidance live in this skill's own `references/` files.) **The skill MUST auto-configure the MCP for the user as the very first action** when the skill is invoked (before asking for an API key, before anything else). @@ -98,3 +99,141 @@ yarn add @yieldxyz/sdk For other languages, use the REST API directly — no SDK needed. The full OpenAPI spec is at `https://api.yield.xyz/docs.json`. + +--- + +## Choosing your integration approach + +Pick the integration approach that matches the use case. When in doubt, default to the +TypeScript SDK. + +### Widget Component (`@stakekit/widget`) + +Drop-in React component that handles the entire yield flow — discovery, selection, +transaction signing, and position tracking. + +- **Best for:** Consumer wallets, quick prototypes, MVPs, and any product that wants + yield functionality without custom UI. +- **Tradeoffs:** Less customization. You get the StakeKit UI. Good for speed, less + control over look and feel. +- **Match signals:** widget, drop-in, component, React component, quick start, fastest, + prototype, MVP. +- **Get started:** + +```tsx +npm install @stakekit/widget // requires React 19+ — see common-pitfalls.md #14 + +import "@stakekit/widget/package/css"; +import { SKApp, darkTheme } from "@stakekit/widget"; + +function YieldPage() { + return ; +} +``` + +The React component is `SKApp`. For a non-React app, use the bundled build: +`import { renderSKWidget } from "@stakekit/widget/bundle"`. + +- **Resources:** + - [Widget Documentation](https://docs.yield.xyz/docs/widget) + - [widget repo](https://github.com/stakekit/widget) + +### TypeScript SDK (`@yieldxyz/sdk`) — Recommended Default + +Typed SDK with methods for all API endpoints. Handles auth, request formatting, and +type safety. The best developer experience for most use cases. + +- **Best for:** Consumer wallets, frontend apps, mobile apps, and any + TypeScript/JavaScript project that wants typed access. Start here unless you have a + specific reason to use something else. +- **Tradeoffs:** TypeScript/JavaScript only. Covers all endpoints. Good balance of + control and convenience. For other languages, use the REST API directly. +- **Match signals:** sdk, typescript, npm, typed, client, frontend, wallet, consumer, + mobile, app. +- **Get started:** + +```typescript +npm install @yieldxyz/sdk + +import { sdk } from "@yieldxyz/sdk"; + +// The SDK is a configured singleton — call configure() once at startup. +sdk.configure({ apiKey: "YOUR_KEY" }); + +// Discover yields +const yields = await sdk.api.getYields({ network: "ethereum" }); + +// Enter a position (amounts are human-readable strings, not wei) +const action = await sdk.api.enterYield({ + yieldId: "ethereum-eth-lido-staking", + address: "0x...", + arguments: { amount: "1.0" }, +}); +``` + +- **Resources:** + - [SDK on npm](https://www.npmjs.com/package/@yieldxyz/sdk) + - [sdk repo](https://github.com/stakekit/sdk) + - [Getting Started](https://docs.yield.xyz/docs/getting-started) + - [Core Concepts](https://docs.yield.xyz/docs/core-concepts) + - [API Recipes](https://github.com/stakekit/api-recipes) + +### Direct REST API + +Full REST API access. Works with any language. Maximum control over every request and +response. + +- **Best for:** Custody platforms, enterprise backends, non-JavaScript projects, and any + product needing maximum control. +- **Tradeoffs:** More code to write. No type safety unless you generate clients. Full + control over everything. +- **Match signals:** custody, institutional, server, backend, enterprise, api, rest, + python, go, java, ruby, rust, non-JS. +- **Get started:** + +No SDK required — every endpoint is plain HTTP, so this is the right path when the +agent isn't writing TypeScript (Python, Go, Rust, Ruby, shell, …). + +```bash +# 1. Discover yields +curl -X GET "https://api.yield.xyz/v1/yields?network=ethereum" \ + -H "x-api-key: YOUR_KEY" \ + -H "Accept: application/json" + +# 2. Build an enter action (returns unsigned transactions to sign + broadcast yourself). +# Amounts are human-readable strings, not wei. +curl -X POST "https://api.yield.xyz/v1/actions/enter" \ + -H "x-api-key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "yieldId": "ethereum-eth-lido-staking", + "address": "0x...", + "arguments": { "amount": "1.0" } + }' +``` + +> Confirm exact field names per endpoint with `yield_get_api_spec` — the request body +> above is illustrative, and the spec is the source of truth. + +- **Resources:** + - [API Reference](https://docs.yield.xyz/reference/getting-started-with-your-api) + - [Core Concepts](https://docs.yield.xyz/docs/core-concepts) + - [API Recipes](https://github.com/stakekit/api-recipes) — runnable REST + ethers.js examples (no SDK) + +### Programmatic Access API + REST API + +Admin API for project provisioning, API key lifecycle, yield enablement, and fee +configuration. Combined with the REST API for transactions. + +- **Best for:** Neobanks, fintechs, multi-tenant platforms, and any product that needs + to manage multiple API keys or configure fees programmatically. +- **Tradeoffs:** Most complex setup. Requires understanding of project hierarchy and fee + structures. +- **Match signals:** neobank, fintech, multi-tenant, white-label, SaaS, platform, + provision, manage keys, fee monetization. +- **Get started:** Start with the Programmatic Access Guide to set up your project + hierarchy, then use the REST API for yield operations. +- **Resources:** + - [Programmatic Access Guide](https://docs.yield.xyz/docs/programmatic-access-guide) + - [Fees Overview](https://docs.yield.xyz/docs/fees) + - [Allocator Vaults](https://docs.yield.xyz/docs/allocator-vaults-oavs-introduction) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index 0efcef7..b07d2fa 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -6,6 +6,80 @@ do not hardcode signing logic from snippets. --- +## Chain family overview (transaction formats) + +This covers the `unsignedTransaction` format returned by the Yield.xyz API for each **chain family**. + +> **All 90+ networks collapse into one of these families for signing purposes.** +> Every network has a `category` field in `GET /v1/networks` — use that to determine which family's signing guide applies. +> For the full per-chain signing example, see `references/chains/.md`. + +| Family | `category` in API | Signing SDK | Networks (examples) | +|--------|-------------------|-------------|---------------------| +| EVM | `evm` | ethers.js, viem | ethereum, base, arbitrum, optimism, polygon, binance, avalanche-c, linea, zksync, sonic, gnosis, celo, fantom, core, monad, unichain + all other EVM chains | +| Cosmos | `cosmos` | @cosmjs/stargate | cosmos, osmosis, celestia, dydx, injective, sei, axelar, kava + 40 more | +| Substrate | `substrate` | @polkadot/api | polkadot, bittensor (Bittensor requires `subnetId`) | +| Solana | `misc` (id=solana) | @solana/web3.js | solana, solana-devnet | +| Tezos | `misc` (id=tezos) | @taquito/taquito | tezos | +| TON | `misc` (id=ton) | @ton/ton | ton, ton-testnet | +| Near | `misc` (id=near) | near-api-js | near | +| Sui | `misc` (id=sui) | @mysten/sui.js | sui | +| Aptos | `misc` (id=aptos) | aptos (official SDK) | aptos | +| Cardano | `misc` (id=cardano) | @emurgo/cardano-serialization-lib | cardano | +| Stellar | `misc` (id=stellar) | stellar-sdk | stellar, stellar-testnet | +| Tron | `misc` (id=tron) | tronweb | tron | + +### Transaction format per family + +> **Chain-specific arguments are FLAT keys inside `arguments`** — e.g. `arguments.cosmosPubKey`, +> `arguments.tezosPubKey`, `arguments.validatorAddresses`, `arguments.tronResource`, +> `arguments.subnetId`. There is **no `additionalAddresses` wrapper.** Always confirm +> the exact required fields from the yield's `mechanics.arguments.enter` schema. + +| Family | `unsignedTransaction` encoding | Parse before signing | Chain-specific arguments | +|--------|--------------------------------|----------------------|--------------------------| +| EVM | JSON string | Yes — `JSON.parse()` | None for per-wallet signing. **But** `liquidity_pool`, `concentrated_liquidity_pool`, and ERC-4626 `vault` yields have TYPE-specific arguments — see `references/chains/evm.md` | +| Cosmos | Hex-encoded Protobuf SignDoc bytes | No — hex decode, pass to signing SDK | `cosmosPubKey` (required, flat key in `arguments`) | +| Substrate | JSON object with call data | Yes — `JSON.parse()` if string | Bittensor: `subnetId` (required, flat key in `arguments`) | +| Solana | Hex or base64 encoded serialized transaction | No — decode, pass to signing SDK | None | +| Tezos | Hex-encoded forged operation bytes | No — hex decode, pass to signing SDK | `tezosPubKey` (required, flat key in `arguments`) | +| TON | JSON string with BOC (Bag of Cells) data | Yes — `JSON.parse()` | None | +| Near | JSON string with transaction object | Yes — `JSON.parse()` | None | +| Sui | Base64-encoded BCS transaction bytes | No — base64 decode | None | +| Aptos | JSON object with transaction payload | Yes — `JSON.parse()` if string | None | +| Cardano | Hex-encoded CBOR transaction bytes | No — hex decode | None | +| Stellar | Base64-encoded XDR transaction envelope | No — base64 decode | None | +| Tron | JSON string with transaction object | Yes — `JSON.parse()` | `validatorAddresses` (array) + `tronResource` (`"BANDWIDTH"` or `"ENERGY"`), flat keys in `arguments` | + +### Per-chain signing & transaction format + +For the full signing example, required arguments, and common gotchas for each chain, +see the per-chain guides: + +- EVM (Ethereum, Base, Arbitrum, Optimism, Polygon, …) — `references/chains/evm.md` +- Cosmos (ATOM, Osmosis, Celestia, dYdX, Injective, Sei, …) — `references/chains/cosmos.md` +- Substrate (Polkadot, Bittensor) — `references/chains/substrate.md` +- Solana — `references/chains/solana.md` +- Tezos — `references/chains/tezos.md` +- TON — `references/chains/ton.md` +- Near — `references/chains/near.md` +- Sui — `references/chains/sui.md` +- Aptos — `references/chains/aptos.md` +- Cardano — `references/chains/cardano.md` +- Stellar — `references/chains/stellar.md` +- Tron — `references/chains/tron.md` + +### Unknown network? + +If you encounter a network not listed above: +1. Call `GET /v1/networks` and check its `category` field +2. `category: "evm"` → use the EVM guide +3. `category: "cosmos"` → use the Cosmos guide +4. `category: "substrate"` → use the Substrate guide +5. `category: "misc"` → match by network `id` to the relevant chain guide + +--- + ## General Principles Regardless of wallet or chain: @@ -115,8 +189,9 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | **CosmosKit** | Multi-wallet abstraction for React | https://cosmoskit.com | ### Cosmos-Specific Requirement -Cosmos yields require the user's **public key** as an additional argument when calling the -action endpoint. Include `additionalAddresses.cosmosPubKey` in the request body. +Cosmos yields require the user's **public key** as an argument when calling the action +endpoint. Include `cosmosPubKey` as a **flat key inside `arguments`** in the request body +(`arguments.cosmosPubKey`) — there is **no `additionalAddresses` wrapper.** Fetch the current schema from the API spec to confirm the exact field name. --- @@ -131,8 +206,31 @@ Fetch the current schema from the API spec to confirm the exact field name. | **TronLink** | Browser wallet (most popular Tron wallet) | https://docs.tronlink.org | ### Tron-Specific Requirement -Tron staking requires specifying the resource type (`"BANDWIDTH"` or `"ENERGY"`) in the -action arguments. Check the yield schema to confirm. +Tron staking requires `validatorAddresses` (a plural **array**) and the resource type +`tronResource` (`"BANDWIDTH"` or `"ENERGY"`) as flat keys inside `arguments`. +Check the yield schema to confirm. + +--- + +## Validator Object Shape (All Chains) + +`GET /v1/yields/{yieldId}/validators` returns an array of validator objects. The +stable fields, identical across every staking chain, are: + +| Field | Type | Notes | +|---|---|---| +| `address` | string | Validator address (chain-specific format) | +| `name` | string | Display name | +| `preferred` | boolean | Curated/recommended validator | +| `commission` | number | **Decimal fraction** — `0.08` means 8% | +| `votingPower` | number | Decimal fraction of total stake | +| `status` | string | `"active"`, `"not_found"`, … | +| `providerId` | string | Validator provider id | +| `rewardRate` | object | `{ total, rateType: "APR", components }` | + +> **There is no `apr` field.** The validator's APR is `rewardRate.total` (a decimal +> fraction; `rateType` is `"APR"`). Per-chain additions: **Polkadot** adds `nominatorCount`; +> **Bittensor** adds a `subnet {}` object. --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md new file mode 100644 index 0000000..7b57cd2 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md @@ -0,0 +1,350 @@ +# Transaction Lifecycle Guide + +Complete step-by-step guide for executing transactions through the Yield.xyz API. + +## Overview + +``` +Discover → Schema → Action → Sign → Broadcast → Submit Hash → Confirm +``` + +Every Yield.xyz transaction follows this lifecycle. Here's each step in detail. + +## Step 1: Discover Yields + +Find available yield opportunities: + +```typescript +// REST API +const response = await fetch("https://api.yield.xyz/v1/yields?network=ethereum&token=USDC", { + headers: { "x-api-key": API_KEY }, +}); +const yields = await response.json(); + +// SDK +const yields = await sdk.api.getYields({ network: "ethereum", token: "USDC" }); +``` + +Filter results by: +- `network` — blockchain network +- `token` — token symbol +- `status.enter` — only yields accepting new deposits +- `apy` — annual percentage yield + +## Step 2: Read the Schema + +Before calling any action, ALWAYS read the yield's mechanics to know required arguments: + +```typescript +// REST API +const yield_ = await fetch(`https://api.yield.xyz/v1/yields/${yieldId}`, { + headers: { "x-api-key": API_KEY }, +}).then(r => r.json()); + +// Check required arguments +const enterArgs = yield_.mechanics.arguments.enter; +console.log(enterArgs); +// { amount: "string (required)", validatorAddress: "string (required for staking)", ... } + +// Check entry limits +console.log(yield_.entryLimits); +// { minimum: "0.01", maximum: "1000000" } +``` + +## Step 3: Call Action Endpoint + +Request the unsigned transactions: + +```typescript +// REST API — Enter a position +const action = await fetch("https://api.yield.xyz/v1/actions/enter", { + method: "POST", + headers: { + "x-api-key": API_KEY, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + yieldId: "ethereum-eth-lido-staking", + address: "0xYourWallet", + arguments: { + amount: "1.0", // Human-readable — 1.0 ETH + }, + }), +}).then(r => r.json()); + +// SDK +const action = await sdk.api.enterYield({ + yieldId: "ethereum-eth-lido-staking", + address: "0xYourWallet", + arguments: { amount: "1.0" }, +}); + +// action.transactions is an array of TransactionDto[] +``` + +### Action Types +- `POST /v1/actions/enter` — enter a yield position +- `POST /v1/actions/exit` — exit a yield position +- `POST /v1/actions/manage` — manage (claim, restake, etc.) + +All three return **HTTP 201** on success (not 200). + +## Step 4: Handle Unsigned Transactions + +These endpoints return an **action** object that wraps an array of transactions. Distinguish the two levels — they each have their own `type` and `executionPattern`. + +The **action** object has these top-level fields: + +```typescript +interface ActionDto { + id: string; + intent: string; // "enter" | "exit" | "manage" + type: string; // "STAKE" for enter, "UNSTAKE" for exit — + // applies even to lending/vault yields (surprising naming) + executionPattern: string; // e.g. "synchronous" + status: string; // "CREATED" initially + transactions: TransactionDto[]; + // ...plus yieldId, address, amount, amountRaw, amountUsd, rawArguments, createdAt, completedAt +} +``` + +Each entry of `action.transactions` is a `TransactionDto`: + +```typescript +interface TransactionDto { + id: string; // Transaction ID (for submit-hash) + title: string; // e.g. "APPROVAL Transaction" + network: string; // e.g., "ethereum" + status: string; // "CREATED" initially + type: string; // "APPROVAL", "SUPPLY", "STAKE", etc. + hash: string | null; // null until broadcast + stepIndex: number; // Execution order + gasEstimate: { // { amount, gasLimit, token } + amount: string; + gasLimit: string; + token: object; + }; + executionPattern: null; // null at the transaction level (it lives on the action) + unsignedTransaction: string; // THE TRANSACTION TO SIGN +} +``` + +**Critical rules:** +1. Execute in `stepIndex` order (0, then 1, then 2...) +2. Wait for CONFIRMED status before proceeding to next +3. NEVER modify `unsignedTransaction` + +## Step 5: Sign the Transaction + +The format of `unsignedTransaction` depends on the chain. See the Chain Guide tool for specifics. + +### EVM — Server-Side Signing (backend / custody) +```typescript +import { ethers } from "ethers"; + +for (const tx of action.transactions) { + // EVM: unsignedTransaction is a JSON string — always JSON.parse() first + const parsed = JSON.parse(tx.unsignedTransaction); + + // Sign and send — DO NOT MODIFY any field + const txResponse = await wallet.sendTransaction(parsed); + const receipt = await txResponse.wait(); + // receipt.hash is what you need for step 6 +} +``` + +### EVM — Browser Wallet Signing (MetaMask / Phantom EVM) + +> **Use this section when signing in the browser with an injected wallet.** +> The server-side example above does not work with MetaMask or Phantom EVM. + +**Three required adjustments before sending to a browser wallet:** + +1. `unsignedTransaction` is a JSON string — always `JSON.parse()` it first. +2. Strip `nonce`, `type`, and `chainId` — the wallet manages these itself. Keeping the API-returned values causes stale simulation and triggers "likely to fail" warnings or on-chain reverts. +3. Use `ethers.js BrowserProvider` — it automatically handles `gasLimit` → `gas` renaming and hex encoding. Do not pass the raw parsed object to `eth_sendTransaction` directly. + +```typescript +import { BrowserProvider } from "ethers"; + +// Works with MetaMask (window.ethereum) or Phantom EVM (window.phantom.ethereum) +const evmProvider = window.phantom?.ethereum ?? window.ethereum; +const provider = new BrowserProvider(evmProvider); +const signer = await provider.getSigner(); + +for (const tx of action.transactions.sort((a, b) => a.stepIndex - b.stepIndex)) { + const parsed = JSON.parse(tx.unsignedTransaction); + + // Strip fields the wallet manages — keeping them causes wrong simulation + const { nonce, type, chainId, ...rest } = parsed; + const txToSend = { + to: rest.to, + data: rest.data, + value: rest.value ?? "0x0", + gasLimit: rest.gasLimit, // ethers.js renames this to gas automatically + maxFeePerGas: rest.maxFeePerGas, + maxPriorityFeePerGas: rest.maxPriorityFeePerGas, + }; + + let hash: string; + try { + const txResponse = await signer.sendTransaction(txToSend); + hash = txResponse.hash; + } catch (err) { + // Browser wallet errors are plain objects {code, message} — not Error instances + const msg = (err as any)?.message ?? JSON.stringify(err); + throw new Error(msg); + } + + // DO NOT use provider.waitForTransaction(hash) — it hangs indefinitely + // with browser-injected providers. Use manual polling instead: + const deadline = Date.now() + 120_000; // 2 minute timeout + let receipt = null; + while (Date.now() < deadline) { + receipt = await provider.getTransactionReceipt(hash); + if (receipt !== null) break; + await new Promise(r => setTimeout(r, 3000)); + } + if (!receipt) throw new Error("Timed out waiting for confirmation"); + if (receipt.status === 0) throw new Error(`Transaction reverted on-chain. Hash: ${hash}`); + + // Submit hash — mandatory (step 6) + await submitHash(tx.id, hash); +} +``` + +**Browser wallet error handling:** +MetaMask and Phantom EVM throw errors as plain objects `{ code: number, message: string }`, not `Error` instances. Always extract the message safely: +```typescript +function extractError(err: unknown): string { + if (err instanceof Error) return err.message; + if (typeof err === "object" && err !== null) { + const e = err as Record; + // Browser-wallet errors and API error bodies both expose `message` directly. + if (typeof e.message === "string") return e.message; + return JSON.stringify(e); + } + return String(err); +} +``` + +> **API error body shape:** `{ statusCode, timestamp, path, message, validation?, details? }`. +> There is **no top-level `error` field** — read `message` (and optionally `validation`/`details`). +> Do not write extractors that read `e.error.message`. + +## Step 6: Submit Hash (MANDATORY) + +After broadcasting each transaction, report the hash back to Yield.xyz: + +```typescript +// REST API +await fetch(`https://api.yield.xyz/v1/transactions/${tx.id}/submit-hash`, { + method: "PUT", + headers: { + "x-api-key": API_KEY, + "Content-Type": "application/json", + }, + body: JSON.stringify({ hash: receipt.hash }), +}); + +// SDK +await sdk.api.submitTransactionHash(tx.id, { hash: receipt.hash }); +``` + +**Why this is mandatory:** Without submitting the hash, Yield.xyz can't track the transaction status, and your balances/positions won't update. + +## Step 7: Confirm and Monitor + +After submitting the hash, the transaction moves through statuses: + +``` +CREATED → WAITING_FOR_SIGNATURE → SIGNED → BROADCASTED → PENDING → CONFIRMED (or FAILED) +``` + +The full set of transaction status values (10 total): +`NOT_FOUND`, `CREATED`, `BLOCKED`, `WAITING_FOR_SIGNATURE`, `SIGNED`, `BROADCASTED`, `PENDING`, `CONFIRMED`, `FAILED`, `SKIPPED`. + +Note it is `WAITING_FOR_SIGNATURE`, **not** `WAITING_FOR_SIGNING`. + +Check status: +```typescript +// Poll for confirmation +const status = await fetch( + `https://api.yield.xyz/v1/transactions/${tx.id}`, + { headers: { "x-api-key": API_KEY } } +).then(r => r.json()); +``` + +## Multi-Step Transaction Example + +EVM actions often return multiple transactions (e.g., approve + deposit): + +```typescript +const action = await sdk.api.enterYield({ + yieldId: "base-usdc-aave-v3-lending", + address: walletAddress, + arguments: { amount: "1000" }, +}); + +// action.transactions might be: +// [0] APPROVAL (stepIndex: 0) — approve USDC spending +// [1] SUPPLY (stepIndex: 1) — deposit into Aave + +for (const tx of action.transactions.sort((a, b) => a.stepIndex - b.stepIndex)) { + const parsed = JSON.parse(tx.unsignedTransaction); + + // Server-side: pass directly to your signer + // Browser wallet: strip nonce/type/chainId first — see "Browser Wallet Signing" section above + const txResponse = await wallet.sendTransaction(parsed); + + // Server-side: .wait() is fine + // Browser wallet: use manual polling — .wait() hangs with injected providers + const receipt = await txResponse.wait(); + + await sdk.api.submitTransactionHash(tx.id, { hash: receipt.hash }); + + // MUST wait for CONFIRMED before executing the next stepIndex +} +``` + +## Pending Actions + +After entering a position, check for pending actions (follow-up transactions). + +Query balances via `POST /v1/yields/balances` with a `queries` array. The response is +`{ items, errors }`, where each `items[]` entry is `{ yieldId, balances, rewardRate }`. +`pendingActions` is nested **per-balance** inside `items[].balances[]` — not at the top level. + +```typescript +// REST API +const res = await fetch("https://api.yield.xyz/v1/yields/balances", { + method: "POST", + headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, + body: JSON.stringify({ + queries: [{ network: "base", address: walletAddress }], + }), +}).then(r => r.json()); + +// res shape: +// { +// items: [ +// { +// yieldId: "base-usdc-aave-v3-lending", +// balances: [{ /* ...balance fields... */, pendingActions: [...] }], +// rewardRate: { /* ... */ }, +// }, +// ], +// errors: [], +// } + +for (const item of res.items) { + for (const balance of item.balances) { + if (balance.pendingActions?.length > 0) { + // User needs to complete these (e.g., claim rewards) + for (const pending of balance.pendingActions) { + console.log(`Pending: ${pending.type} — ${pending.description}`); + } + } + } +} +``` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md new file mode 100644 index 0000000..79bca66 --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md @@ -0,0 +1,158 @@ +# `GET /v1/yields` — Endpoint & Yield Types Guide + +This is the single most important endpoint in the Yield.xyz API. It's the discovery surface for every yield opportunity across 80+ networks, and almost every app you build starts with a call to it. This guide covers both: + +1. **How to query it** — all the filter, search, sort, and pagination params, with the optimizations the server does for you. +2. **What the yield types mean** — the `type` dimension at a high level, with the yield DTO as the source of truth for any specific yield. + +--- + +## Part 1 — Querying `/v1/yields` + +> **Golden rule:** `GET /v1/yields` does filtering, multi-chain merging, searching, and sorting **on the server**. You almost never need `for each chain, fetch + merge + sort client-side`. A single call with the right params replaces that whole pattern. + +### Full parameter reference + +| Param | Type | Example | Purpose | +|---|---|---|---| +| `network` | single enum | `base` | Filter to one network | +| `networks` | comma-separated | `base,arbitrum,optimism` | **Multi-chain query in one call** | +| `chainId` | string (EVM) | `1` | Filter by EVM chain id (Ethereum: `1`, Polygon: `137`, Base: `8453`, etc.) | +| `yieldId` | single string | `base-usdc-aave-v3-lending` | Look up one yield | +| `yieldIds` | comma-separated (≤100) | `base-usdc-aave-v3-lending,ethereum-eth-lido-staking` | Batch-lookup up to 100 yields — replaces 100 individual `/v1/yields/{id}` calls | +| `type` | single `YieldType` | `lending` | Filter to one yield type | +| `types` | comma-separated | `lending,vault,staking` | **Multi-type query in one call** | +| `token` | string | `USDC` or token address | Filter by deposit/input token (symbol or address) | +| `inputToken` | string | `USDC` | Alias/alternative to `token` | +| `inputTokens` | comma-separated | `USDC,USDT,DAI` | **Multi-token query in one call** | +| `provider` | string | `aave-v3` | Filter by a single protocol | +| `providers` | comma-separated | `aave-v3,morpho,compound-v3` | **Multi-provider query in one call** | +| `hasCooldownPeriod` | boolean | `true` / `false` | Only yields with (or without) an unbonding cooldown | +| `hasWarmupPeriod` | boolean | `true` / `false` | Only yields with (or without) a warmup period | +| `search` | string | `aave` | **Server-side name search** — don't `.filter()` on the client | +| `sort` | enum (see below) | `rewardRateDesc` | **Server-side sort** — don't `.sort()` on the client | +| `limit` | integer | `20` | Page size | +| `offset` | integer | `0` | Paging cursor | + +### Sort options (`YieldSortingOption` enum) + +| Value | Meaning | +|---|---| +| `rewardRateDesc` | **Sort by APY descending** — the one you want for "top yields" UIs | +| `rewardRateAsc` | Sort by APY ascending | +| `statusEnterDesc` / `statusEnterAsc` | Sort by whether entry is currently available | +| `statusExitDesc` / `statusExitAsc` | Sort by whether exit is currently available | + +### Response shape + +```json +{ + "total": 1847, // total matching the filter — use this for "X of N" UI labels + "limit": 20, + "offset": 0, + "items": [ + { + "id": "base-usdc-aave-v3-lending", + "network": "base", + "chainId": 8453, + "inputTokens": [{ "symbol": "USDC", "address": "0x...", "network": "base", "decimals": 6 }], + "tokens": [ ... ], + "rewardRate": { "total": 0.0542, "rateType": "APY" }, + "status": { "enter": true, "exit": true }, + "mechanics": { + "type": "lending", + "requiresValidatorSelection": false, + "entryLimits": { ... }, + "arguments": { "enter": { "fields": [ ... ] }, "exit": { "fields": [ ... ] } } + }, + "providerId": "aave-v3", + "metadata": { "name": "...", "logoURI": "...", "deprecated": false } + } + ] +} +``` + +**Field names are live-spec-driven** — check `yield_get_api_spec({ endpoint: "/v1/yields" })` or `curl https://api.yield.xyz/docs.json` for the authoritative shape at the time you build. (The list response carries no current-TVL field — use the `/v1/yields/{id}/tvl/history` endpoint for TVL.) + +### Common query patterns + +Every example below is **one** HTTP call. The server does the fan-out. + +**Top 20 USDC yields across Base + Arbitrum + Optimism, lending or vault, sorted by APY desc:** +``` +GET /v1/yields?networks=base,arbitrum,optimism&inputToken=USDC&types=lending,vault&sort=rewardRateDesc&limit=20 +``` + +**All stablecoin yields (USDC/USDT/DAI) on Ethereum, sorted by APY desc:** +``` +GET /v1/yields?network=ethereum&inputTokens=USDC,USDT,DAI&sort=rewardRateDesc&limit=50 +``` + +**Search: yields whose name contains "morpho", sorted by APY desc:** +``` +GET /v1/yields?search=morpho&sort=rewardRateDesc +``` + +**Batch-fetch 20 specific yield IDs in one call** (instead of 20 individual `/v1/yields/{id}` calls): +``` +GET /v1/yields?yieldIds=base-usdc-aave-v3-lending,ethereum-eth-lido-staking,... +``` + +**Only yields with no lockup (instant exit):** +``` +GET /v1/yields?hasCooldownPeriod=false&sort=rewardRateDesc +``` + +**Highest-rate staking across all chains:** +``` +GET /v1/yields?type=staking&sort=rewardRateDesc&limit=50 +``` + +### When NOT to fan out on the client + +If you find yourself writing any of these patterns, stop and use a server-side param instead: + +| Anti-pattern | Replace with | +|---|---| +| `for (const chain of chains) { fetch(...&network=${chain}) }` then merge | `?networks=chain1,chain2,chain3` | +| `fetch(...).then(res => res.items.sort((a,b) => b.apy - a.apy))` | `?sort=rewardRateDesc` | +| `fetch(...).then(res => res.items.filter(y => y.name.includes(q)))` | `?search=${q}` | +| `fetch(...&type=lending); fetch(...&type=vault)` merged | `?types=lending,vault` | +| 100 calls to `/v1/yields/{id}` for a portfolio view | `?yieldIds=id1,id2,...,id100` | + +Client-side sort across paginated data is always wrong — page 2's items don't know page 1's sort context. Always sort on the server. + +--- + +## Part 2 — Yield Types (high level) + +The `type` field (and the `type` / `types` filters) uses the `YieldType` enum. Values are case-sensitive **snake_case** — confirm the current set with `yield_get_api_spec`: + +``` +staking liquid_staking restaking lending vault +real_world_asset concentrated_liquidity_pool liquidity_pool +``` + +> **Casing matters:** pass `type=real_world_asset`, not `real-world-asset` — a kebab-case value won't match. + +**The type is only a high-level category.** It tells you roughly what a yield does — it does **not** define a yield's exact inputs, flow, lock period, fees, or receipt token, and two yields of the same type can differ. For any specific yield, the **yield DTO is the source of truth**: + +> `GET /v1/yields/{yieldId}` → read `mechanics.arguments.enter` / `.exit` for the exact required fields, plus `cooldownPeriod`, fees, `status.enter` / `status.exit`, and `entryLimits`. Never assume a yield's behavior from its type alone, and always read `mechanics.arguments.enter` before calling `actions_enter` — that's the contract for what to send. + +| Type | What it is (high level) | Often involves — **confirm in the DTO** | +|------|-------------------------|------------------------------------------| +| `staking` | Delegate to a validator to secure a proof-of-stake network | Validator selection; unbonding delay on exit | +| `liquid_staking` | Stake and receive a liquid/receipt token in return | A receipt token; typically no validator selection | +| `restaking` | Restake staked assets to secure additional services (AVS) | Withdrawal queue / escrow on exit | +| `lending` | Supply tokens to a lending market to earn interest | Single-token deposit | +| `vault` | Automated strategy vault (often ERC-4626) | Single-token deposit, share token | +| `liquidity_pool` | Provide liquidity to a DEX pool for trading fees | Two token amounts; impermanent-loss exposure | +| `concentrated_liquidity_pool` | Provide liquidity within a chosen price range | Two tokens + a price range; position as an NFT | +| `real_world_asset` | On-chain exposure to off-chain assets (treasuries, credit, …) | KYC gating; redemption windows | + +The right-hand column is a *hint* for what to expect — not a contract. The DTO is authoritative for every specific. + +> **`real_world_asset` is often KYC-gated.** Before entering, check +> `GET /v1/yields/{id}/kyc/status?address=` and gate the Enter action on +> `kycStatus === "approved"` (otherwise redirect the user to the returned +> `authorizeUrl`). See the RWA / KYC builder flow in `integration-patterns.md`. From 43c8352313569d92971d11307f90c7f50ed133f3 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:14:53 +0200 Subject: [PATCH 06/23] feat(builder-skill): close end-to-end integration gaps (any system, any language) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dogfood pass — simulated real integrations (Python backend, React/viem wallet, non-EVM signing, onboarding) against live prod and closed the gaps that blocked a zero→working build. Correctness: - remove fabricated webhook guidance (no webhook/event endpoints exist) → poll GET /v1/transactions/{id}; mark rate-limit tiers illustrative (confirm via header) - remove fabricated POST /v1/programmatic/.../fee-config endpoint → fees are dashboard-configured, applied server-side (reconcile with dashboard-and-api-keys.md) - executionPattern has 3 values (synchronous/asynchronous/batch) — branch on it instead of always sequential-waiting - gasEstimate is a JSON string (parse it); mechanics.arguments.enter is .fields[] (array), entryLimits under mechanics and nullable - reconcile L2 gas handling (strip gas fields on L2, keep on mainnet) across files Completeness: - Quickstart spine in SKILL.md (discover→schema→enter→sign→submit-hash→poll); route to chains/ and api-field-mapping.md from the reference table - transaction-lifecycle: polling loop + terminal states, multi-step sequencing on Yield CONFIRMED, two broadcast paths (submit-hash vs submit), idempotency/retries - EVM signing for Python (eth-account: drop `from`, gasLimit→gas) and viem/wagmi (gasLimit→gas, useWaitForTransactionReceipt); clarify "never modify" = semantics, not library key-shape; note executionMode individual|batched for smart accounts - runnable non-EVM signing: Cosmos (SignDoc.decode→signDirect→TxRaw→broadcast, real bech32 pubkey, sign SignDoc verbatim), Tron (v6 named import + confirmation poll), Solana (bs58 key import, unified deserialize/sign/submit) - testnets in setup.md (base-sepolia/ethereum-sepolia/stellar-testnet; match by id) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/SKILL.md | 31 +++ .../references/api-limits.md | 24 ++- .../references/chains/cosmos.md | 52 ++++- .../references/chains/evm.md | 70 +++++- .../references/chains/solana.md | 59 +++-- .../references/chains/tron.md | 30 ++- .../references/integration-patterns.md | 18 +- .../references/setup.md | 14 ++ .../references/signing-patterns.md | 18 +- .../references/transaction-lifecycle.md | 204 ++++++++++++++---- 10 files changed, 414 insertions(+), 106 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 73dc097..93dcae2 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -25,6 +25,35 @@ Perps/Borrow, lean on the live spec and keep those two differences in mind. --- +## Quickstart — minimal end-to-end (Yield) + +The shortest happy path from nothing to a confirmed position. Language-agnostic REST; +swap `curl` for `fetch`/`requests`/etc. Always confirm exact field names against the +live spec (`yield_get_api_spec` or `https://api.yield.xyz/docs.json`) before generating +code — the shapes below are a map, not a contract. + +1. **Discover** — `GET /v1/yields?network=&token=` to find a yield id. +2. **Read the schema** — `GET /v1/yields/{id}` and read + `mechanics.arguments.enter.fields[]` to learn exactly which arguments the enter + action requires (e.g. `amount`, sometimes a validator address). +3. **Build the action** — `POST /v1/actions/enter` with `{ yieldId, address, arguments }` + (amounts are human-readable strings, not wei). **Returns HTTP 201** with a + `transactions[]` array. +4. **Sign + broadcast each transaction** — iterate `transactions[]` in **`stepIndex` + order**; for each, parse and sign its `unsignedTransaction` with the wallet for that + chain, then broadcast. Never modify `unsignedTransaction` (Critical Rule #5). +5. **Report the hash** — `PUT /v1/transactions/{id}/submit-hash` with the broadcast + hash so Yield.xyz can track it. +6. **Poll to confirm** — `GET /v1/transactions/{id}` until status is `CONFIRMED` + (there are no webhooks — poll with backoff). + +For the full lifecycle (status states, error/retry handling, exit/manage) see +[`references/transaction-lifecycle.md`](./references/transaction-lifecycle.md); for +per-chain signing and transaction decoding see +[`references/chains/.md`](./references/chains/). + +--- + ## When This Skill Activates This skill is for **building apps** — not for exploring yields conversationally. @@ -441,6 +470,8 @@ before generating code for that topic.** | **[`references/mcp-tools.md`](./references/mcp-tools.md)** | **Before invoking any MCP tool** — which tools are safe (doc tools) vs. which must never be called (action tools) | | **[`references/common-pitfalls.md`](./references/common-pitfalls.md)** | **Before generating any code** — known errors and how to avoid them | | **[`references/signing-patterns.md`](./references/signing-patterns.md)** | Before generating signing/wallet code — SDKs, libraries, chain-specific guidance | +| **[`references/chains/.md`](./references/chains/)** | Before generating signing/decoding code for a specific chain (e.g. `evm.md`, `solana.md`, `cosmos.md`, `stellar.md`) — per-chain transaction signing and decoding | +| **[`references/api-field-mapping.md`](./references/api-field-mapping.md)** | When wiring requests/responses — endpoint reference and the error envelope shape | | **[`references/integration-patterns.md`](./references/integration-patterns.md)** | When user describes their product type — architecture per use case | | **[`references/output-formats.md`](./references/output-formats.md)** | When generating UI code — display rules, number formatting | | **[`references/policies.md`](./references/policies.md)** | API usage limits, rate limiting, caching guidance | diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md index 6bcbac9..385b1c4 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md @@ -2,15 +2,21 @@ ## Rate Limit Tiers -| Tier | Requests/min | Requests/day | Use Case | -|------|-------------|-------------|----------| -| Free/Test | 30 | 1,000 | Development, testing, prototyping | -| Production | 300 | 50,000 | Production applications | -| Enterprise | Custom | Custom | High-volume integrations | +> **Illustrative only — not authoritative.** The numbers below are rough examples, +> not your key's real limits. Observed production keys have allowed far higher limits +> (e.g. `x-ratelimit-limit` ~120,000). **Confirm your key's actual limit via the +> `x-ratelimit-limit` response header** rather than relying on this table. + +| Tier | Requests/min (illustrative) | Use Case | +|------|-------------|----------| +| Free/Test | ~30 | Development, testing, prototyping | +| Production | ~300 | Production applications | +| Enterprise | Custom | High-volume integrations | ## How Rate Limits Work - Limits are applied per API key +- The `x-ratelimit-limit` header shows your key's actual limit — treat it as the source of truth - The `x-ratelimit-remaining` header shows remaining requests - The `x-ratelimit-reset` header shows when the limit resets (Unix timestamp) - When exceeded, the API returns `429 Too Many Requests` @@ -62,9 +68,13 @@ Use `GET /v1/yields` with filters instead of fetching individual yields: GET /v1/yields?network=ethereum&token=USDC ``` -### Use Webhooks for Status Updates +### Poll for Status — There Are No Webhooks -Instead of polling transaction status, configure webhooks to receive status change notifications. +Yield.xyz has **no webhook, event, or callback endpoints**. To learn the status of an +in-flight action or transaction, poll `GET /v1/transactions/{id}` (and +`GET /v1/actions/{id}`) until it reaches a terminal state. Use sensible polling +intervals with backoff rather than a tight loop — see `transaction-lifecycle.md` for +the recommended cadence and the full status flow. ## Getting a Production Key diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md index be200e4..4dda360 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md @@ -33,15 +33,22 @@ When calling `POST /v1/actions/enter` for any Cosmos yield, the schema (`mechani ### Getting cosmosPubKey ```typescript -// Using @cosmjs/proto-signing +// Using @cosmjs/proto-signing + @cosmjs/amino import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; +import { encodeSecp256k1Pubkey, pubkeyToAddress } from "@cosmjs/amino"; +import { toBech32, fromBech32 } from "@cosmjs/encoding"; const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "cosmos" }); const [account] = await wallet.getAccounts(); + +// account.pubkey is the raw 33-byte compressed secp256k1 key (Uint8Array). // The API expects the bech32-encoded public key (cosmospub1...), not hex/base64. -const cosmosPubKey = wallet.encodePubkey - ? wallet.encodePubkey(account.pubkey) - : /* bech32-encode account.pubkey with the "cosmospub" prefix */ undefined; +// Derive it by Amino-encoding the pubkey, then bech32-encoding the raw key bytes +// with the chain's "pub" HRP. +const aminoPubkey = encodeSecp256k1Pubkey(account.pubkey); // { type, value: base64 } +const cosmosPubKey = toBech32("cosmospub", account.pubkey); + +const address = account.address; // cosmos1... (used as the `address` field on the action) ``` ```typescript @@ -59,18 +66,43 @@ const action = await sdk.api.enterYield({ ## Signing +> **Sign the API's SignDoc VERBATIM.** The `SignDoc` returned by Yield.xyz already +> embeds `accountNumber`, the account `sequence`, the fee, and the gas. Do **NOT** +> re-fetch the account from the chain or rebuild the tx — decode the bytes, sign them +> as-is, and re-assemble. Rebuilding will produce a signature over different bytes and +> the broadcast will fail with a signature-verification error. + ```typescript -import { SigningStargateClient } from "@cosmjs/stargate"; +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; +import { StargateClient } from "@cosmjs/stargate"; +import { fromHex, fromBase64 } from "@cosmjs/encoding"; +import { SignDoc, TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; + +// OfflineDirectSigner — DirectSecp256k1HdWallet implements signDirect() +const signer = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "cosmos" }); +const [account] = await signer.getAccounts(); +const address = account.address; // cosmos1... + +const client = await StargateClient.connect("https://rpc.cosmos.network:443"); for (const tx of action.transactions) { - // tx.unsignedTransaction is hex-encoded SignDoc bytes - const signDocBytes = Buffer.from(tx.unsignedTransaction, "hex"); + // tx.unsignedTransaction is hex-encoded Protobuf SignDoc bytes. + // Decode VERBATIM — do not rebuild from chain state. + const signDoc = SignDoc.decode(fromHex(tx.unsignedTransaction)); + + // Sign the SignDoc as-is with the OfflineDirectSigner. + const { signed, signature } = await signer.signDirect(address, signDoc); - // Sign with your Cosmos signer - const signed = await signer.signDirect(address, decodeSignDoc(signDocBytes)); + // Assemble the broadcastable TxRaw from the SIGNED doc the signer returned + // (signed.bodyBytes / signed.authInfoBytes are what was actually signed). + const txRawBytes = TxRaw.encode({ + bodyBytes: signed.bodyBytes, + authInfoBytes: signed.authInfoBytes, + signatures: [fromBase64(signature.signature)], + }).finish(); // Broadcast - const result = await client.broadcastTx(signed); + const result = await client.broadcastTx(txRawBytes); // Submit hash back to Yield.xyz — MANDATORY await fetch(`https://api.yield.xyz/v1/transactions/${tx.id}/submit-hash`, { diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md index d061e19..de992ac 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md @@ -52,8 +52,20 @@ Type-specific notes: There is no `cosmosPubKey` equivalent on EVM, but the argument set is still per-yield. **Always confirm the exact fields in `mechanics.arguments.enter.fields[]` (and `.exit.fields[]`) from the yield DTO before building an action.** +- **`executionMode`** (enter/exit): `individual` | `batched`. `batched` returns single-tx calldata for smart accounts (EIP-7702/4337), sidestepping the APPROVAL→SUPPLY two-step. Confirm support in `mechanics.arguments.enter.fields[]`. + ## Signing +> **Never modify `unsignedTransaction` — but understand what "modify" means.** The rule +> forbids changing transaction **semantics**: `to`, `data`, `value`, amounts, gas **values** +> (`gasLimit`/`maxFeePerGas`/`maxPriorityFeePerGas`), and `nonce`. Those must be signed +> exactly as returned. It does **not** forbid library-required **key-shape adaptation** — +> dropping the informational `from` key or renaming `gasLimit` → `gas` so a signer accepts +> the object. Re-keying a field is not the same as changing its value, and some signers +> (eth-account, viem) require it. Adapt the shape; never touch the values. + +### EVM signing — TypeScript (ethers.js v6) + ```typescript // Using ethers.js v6 import { ethers } from "ethers"; @@ -64,7 +76,7 @@ const wallet = new ethers.Wallet(PRIVATE_KEY, provider); for (const tx of action.transactions) { const parsed = JSON.parse(tx.unsignedTransaction); - // Sign and send — DO NOT MODIFY any field + // Sign and send — DO NOT MODIFY any value (ethers maps gasLimit automatically) const txResponse = await wallet.sendTransaction(parsed); const receipt = await txResponse.wait(); @@ -75,8 +87,56 @@ for (const tx of action.transactions) { } ``` +### EVM signing — Python (server-side, eth-account) + +`eth-account` will **not** accept the raw parsed object. Two key-shape adaptations are +required (neither changes a value): (a) **drop the `from` key** — eth-account rejects it +and derives the sender from the private key; (b) **rename `gasLimit` → `gas`** — eth-account +uses `gas`. Hex-string gas values and integer `nonce`/`chainId`/`type` are accepted as-is. + +```python +from eth_account import Account +tx = json.loads(unsigned_transaction) +tx.pop("from", None) # eth-account derives sender from the key +tx["gas"] = tx.pop("gasLimit") # eth-account uses `gas` +signed = Account.from_key(PRIVATE_KEY).sign_transaction(tx) +# broadcast signed.raw_transaction via web3.py eth_send_raw_transaction or any RPC +``` + +After broadcasting, submit the hash — MANDATORY — via `PUT /v1/transactions/{txId}/submit-hash`. + +### EVM signing — viem / wagmi (browser) + +**viem renames `gasLimit` → `gas`** (unlike ethers, which does it automatically). Map the +parsed fields explicitly: `to`/`data` pass through; `value` is **omitted** (not `"0x0"`) +for token operations; gas values become `BigInt`. + ```typescript -// Using viem +import { useWaitForTransactionReceipt, useWalletClient } from "wagmi"; + +const parsed = JSON.parse(tx.unsignedTransaction); +const { data: walletClient } = useWalletClient(); + +const hash = await walletClient.sendTransaction({ + to: parsed.to, + data: parsed.data, + // value omitted for token ops; pass BigInt(parsed.value) only for native transfers + gas: BigInt(parsed.gasLimit), // viem uses `gas`, not `gasLimit` + maxFeePerGas: BigInt(parsed.maxFeePerGas), + maxPriorityFeePerGas: BigInt(parsed.maxPriorityFeePerGas), +}); + +// Confirm receipt with wagmi — works with injected providers, +// unlike ethers `waitForTransaction` which hangs in the browser +const { data: receipt } = useWaitForTransactionReceipt({ hash }); + +await sdk.api.submitTransactionHash(tx.id, { hash }); +``` + +### EVM signing — viem (server-side) + +```typescript +// Using viem with a local account (server-side) import { createWalletClient, http } from "viem"; import { base } from "viem/chains"; @@ -84,7 +144,11 @@ const client = createWalletClient({ chain: base, transport: http() }); for (const tx of action.transactions) { const parsed = JSON.parse(tx.unsignedTransaction); - const hash = await client.sendTransaction(parsed); + // viem uses `gas`, not `gasLimit` — re-key when passing the parsed object + const hash = await client.sendTransaction({ + ...parsed, + gas: BigInt(parsed.gasLimit), + }); await sdk.api.submitTransactionHash(tx.id, { hash }); } diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md index e34b680..1366b3e 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/solana.md @@ -31,51 +31,46 @@ Solana is overwhelmingly **lending + vault** (~251 yields total, mostly lending/ ## Signing ```typescript -import { Connection, Transaction, Keypair } from "@solana/web3.js"; +import { Connection, Transaction, VersionedTransaction, Keypair } from "@solana/web3.js"; import bs58 from "bs58"; -const connection = new Connection("https://api.mainnet-beta.solana.com"); -const keypair = Keypair.fromSecretKey(/* your key */); +const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed"); +// PRIVATE_KEY is the base58-encoded secret key (the Phantom / CLI export format). +const keypair = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY)); for (const tx of action.transactions) { - // Decode the hex-encoded transaction + // Decode the hex-encoded transaction bytes. const txBytes = Buffer.from(tx.unsignedTransaction, "hex"); - const transaction = Transaction.from(txBytes); - // Sign - transaction.sign(keypair); + // Some Solana yields return legacy transactions, others v0 VersionedTransactions. + // Detect which: try v0 first, fall back to legacy. ONLY the deserialize step is + // guarded — signing/broadcast errors must propagate, not be swallowed. + let serialized: Uint8Array; + let versioned: VersionedTransaction | undefined; + try { + versioned = VersionedTransaction.deserialize(txBytes); + } catch { + versioned = undefined; + } + + if (versioned) { + versioned.sign([keypair]); + serialized = versioned.serialize(); + } else { + const legacy = Transaction.from(txBytes); + legacy.sign(keypair); + serialized = legacy.serialize(); + } - // Broadcast - const signature = await connection.sendRawTransaction(transaction.serialize()); - await connection.confirmTransaction(signature); + // Broadcast — both branches converge here. The signature IS the tx hash on Solana. + const signature = await connection.sendRawTransaction(serialized); + await connection.confirmTransaction(signature, "confirmed"); // Submit hash — MANDATORY await sdk.api.submitTransactionHash(tx.id, { hash: signature }); } ``` -### Versioned Transactions - -Some Solana yields use Versioned Transactions (v0). Handle both: - -```typescript -import { VersionedTransaction } from "@solana/web3.js"; - -const txBytes = Buffer.from(tx.unsignedTransaction, "hex"); - -try { - // Try versioned first - const vtx = VersionedTransaction.deserialize(txBytes); - vtx.sign([keypair]); - const sig = await connection.sendRawTransaction(vtx.serialize()); -} catch { - // Fall back to legacy - const ltx = Transaction.from(txBytes); - ltx.sign(keypair); - const sig = await connection.sendRawTransaction(ltx.serialize()); -} -``` - ## Browser Wallet Signing (Phantom) > **Use this section when signing in the browser with Phantom's Solana wallet (`window.phantom.solana`).** diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md index f717315..16db35b 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md @@ -5,6 +5,8 @@ **Encoding:** JSON string with transaction object **Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` +The parsed object is the TronGrid transaction object — `{ txID, raw_data, raw_data_hex, ... }` — exactly the shape `tronWeb.trx.sign(...)` expects. Pass it through unchanged. + ## Required Arguments | Argument | Required | Description | @@ -18,7 +20,10 @@ All arguments are flat keys inside `arguments`. Confirm exact required fields vi ## Signing ```typescript -const TronWeb = require("tronweb"); +// tronweb v6 — TronWeb is a NAMED export. `const TronWeb = require("tronweb")` +// (or a default import) gives the module namespace, and `new TronWeb(...)` throws +// "TronWeb is not a constructor". +const { TronWeb } = require("tronweb"); // ESM: import { TronWeb } from "tronweb"; const tronWeb = new TronWeb({ fullHost: "https://api.trongrid.io", @@ -26,6 +31,7 @@ const tronWeb = new TronWeb({ }); for (const tx of action.transactions) { + // Parsed object is the TronGrid tx ({ txID, raw_data, raw_data_hex, ... }). const txData = JSON.parse(tx.unsignedTransaction); // Sign @@ -33,12 +39,32 @@ for (const tx of action.transactions) { // Broadcast const result = await tronWeb.trx.sendRawTransaction(signedTx); + if (!result.result) { + throw new Error(`Tron broadcast rejected: ${result.code} ${result.message ?? ""}`); + } + const txid = result.txid; + + // Confirm on-chain — a Tron tx can be ACCEPTED at broadcast but still FAIL during + // execution (e.g. OUT_OF_ENERGY). Poll getTransactionInfo until the receipt resolves. + let receiptResult: string | undefined; + for (let attempt = 0; attempt < 30; attempt++) { + const info = await tronWeb.trx.getTransactionInfo(txid); + receiptResult = info?.receipt?.result; // undefined until the tx is included + if (receiptResult) break; + await new Promise((resolve) => setTimeout(resolve, 3000)); + } + if (receiptResult !== "SUCCESS") { + throw new Error(`Tron tx ${txid} did not succeed: ${receiptResult ?? "not confirmed in time"}`); + } // Submit hash — MANDATORY - await sdk.api.submitTransactionHash(tx.id, { hash: result.txid }); + await sdk.api.submitTransactionHash(tx.id, { hash: txid }); } ``` +> **Version:** Pin `tronweb@^6.0.0`. The named-export change landed in v6 — pre-v6 +> default-import code will break on upgrade, and v6 will break the old default import. + ## Common Gotchas 1. **tronResource required**: Always include `tronResource: "BANDWIDTH"` or `"ENERGY"`. Missing this causes a 422 error. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md index 54fe4d5..93a1317 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md @@ -109,14 +109,14 @@ Your App -> Your Backend -> Yield.xyz API (per-tenant API keys) - **Common pitfalls:** See `common-pitfalls.md` — especially entries #1, #2, #6 ### Fee Setup -``` -POST /v1/programmatic/projects/{id}/fee-config -{ - "depositFeePercent": 0.5, - "performanceFeePercent": 15, - "managementFeePercent": 2 -} -``` + +Fees (deposit / performance / management) are configured **per-project in the dashboard** +(or via the separate Programmatic Access API — a different base URL; confirm the exact +shape with the Yield team) and applied **server-side** to the actions built for your key. +There is **no public `fee-config` REST body to POST** on `api.yield.xyz`. See +`dashboard-and-api-keys.md`. Once configured, the fee transactions show up automatically +in the `transactions[]` returned by `POST /v1/actions/enter` — you don't pass fee +percentages in the request. --- @@ -176,7 +176,7 @@ Here `kycStatus !== "approved"`, so gate Enter and send the user to Enterprise integrations need reliability, monitoring, and compliance controls. ### Key Considerations -- **Idempotency:** The API is idempotent for read operations. For actions, use unique request IDs +- **Idempotency:** Read operations are naturally idempotent. **`POST /v1/actions/enter` is NOT** — it has no idempotency-key parameter and `CreateActionDto` has no `referenceId`, so each call mints a *new* action. Dedupe client-side (e.g. a local key on yieldId + address + amount) before calling, and recover from a crash by listing existing actions with `GET /v1/actions?address=` rather than blindly re-calling enter - **Monitoring:** Track API latency, error rates, and transaction success rates - **Disaster recovery:** Cache unsigned transactions and implement retry logic - **Compliance:** Implement configurable guardrails for allowed networks and risk thresholds diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md index 3c2a796..89d34d0 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md @@ -85,6 +85,20 @@ If you get a JSON response with yield data, you're ready to build. --- +## Testing without real funds + +You can smoke-test an integration on a testnet before touching mainnet. Yield.xyz +exposes testnet networks in `GET /v1/networks` — currently `base-sepolia`, +`ethereum-sepolia`, and `stellar-testnet`. **There is no `isTestnet` flag on network +objects**, so identify testnets by matching the network **id** (the `-sepolia` / +`-testnet` suffix), not a boolean field. + +Recommended: run a full enter flow (discover → build action → sign → broadcast → +submit-hash → confirm) against a testnet first, fund the wallet from a public faucet, +and only switch the network id to mainnet once the flow works end-to-end. + +--- + ## Step 4 — Install SDK (optional) For TypeScript/JavaScript projects, the SDK provides typed wrappers: diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index b07d2fa..0352845 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -16,7 +16,7 @@ This covers the `unsignedTransaction` format returned by the Yield.xyz API for e | Family | `category` in API | Signing SDK | Networks (examples) | |--------|-------------------|-------------|---------------------| -| EVM | `evm` | ethers.js, viem | ethereum, base, arbitrum, optimism, polygon, binance, avalanche-c, linea, zksync, sonic, gnosis, celo, fantom, core, monad, unichain + all other EVM chains | +| EVM | `evm` | ethers.js, viem (TS); eth-account + web3.py (Python) | ethereum, base, arbitrum, optimism, polygon, binance, avalanche-c, linea, zksync, sonic, gnosis, celo, fantom, core, monad, unichain + all other EVM chains | | Cosmos | `cosmos` | @cosmjs/stargate | cosmos, osmosis, celestia, dydx, injective, sei, axelar, kava + 40 more | | Substrate | `substrate` | @polkadot/api | polkadot, bittensor (Bittensor requires `subnetId`) | | Solana | `misc` (id=solana) | @solana/web3.js | solana, solana-devnet | @@ -103,6 +103,7 @@ For backends, custody platforms, and any environment where you control the priva | **ethers.js v6** | General-purpose EVM signing | https://docs.ethers.org/v6/ | | **viem** | Type-safe, lightweight alternative to ethers | https://viem.sh | | **web3.js v4** | Legacy projects, broad ecosystem | https://docs.web3js.org | +| **eth-account + web3.py** | Python backends | https://eth-account.readthedocs.io / https://web3py.readthedocs.io | ### Approach - Create a `Wallet` (ethers) or `WalletClient` (viem) with the private key and RPC provider @@ -110,6 +111,11 @@ For backends, custody platforms, and any environment where you control the priva - Pass the parsed object directly to `sendTransaction()` — no modifications - `receipt.wait()` (ethers) or `waitForTransactionReceipt()` (viem) works correctly server-side +**Python (`eth-account` + `web3.py`):** `eth-account` requires two key-shape adaptations +before signing — drop the `from` key and rename `gasLimit` → `gas` (neither changes a +value). Sign with `Account.from_key(...).sign_transaction(tx)`, then broadcast +`signed.raw_transaction`. Full example: `references/chains/evm.md` ("EVM signing — Python"). + --- ## EVM — Browser Wallet Signing @@ -144,10 +150,12 @@ When signing with any browser wallet, these adjustments are required: 1. **`JSON.parse()` the `unsignedTransaction`** — it's a JSON string on EVM 2. **Strip `nonce`, `type`, and `chainId`** — the wallet manages these. Keeping the API-returned values causes stale simulation and triggers "likely to fail" warnings -3. **On L2 chains (Base, Arbitrum, Optimism, Polygon, etc.):** omit gas fields entirely - (`gasLimit`, `maxFeePerGas`, `maxPriorityFeePerGas`). Let the wallet estimate gas — - L2 gas changes rapidly and API values go stale -4. **On Ethereum mainnet:** you can optionally pass gas fields for a better fee estimate UX +3. **On L2 chains (Base, Arbitrum, Optimism, Polygon, etc.):** strip the gas fields + entirely — `gasLimit` (or its `gas` rename), `maxFeePerGas`, `maxPriorityFeePerGas`. + Let the wallet estimate gas; L2 gas changes rapidly and API values go stale. This + applies to **both the ethers and viem paths** (see `transaction-lifecycle.md`) +4. **On Ethereum mainnet:** keep the gas fields — pass them through for a better fee + estimate UX (don't strip on mainnet) 5. **Use manual receipt polling** — `waitForTransaction()` hangs with injected providers 6. **Extract errors defensively** — browser wallets throw plain `{code, message}` objects, not `Error` instances diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md index 7b57cd2..3917425 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md @@ -5,10 +5,12 @@ Complete step-by-step guide for executing transactions through the Yield.xyz API ## Overview ``` -Discover → Schema → Action → Sign → Broadcast → Submit Hash → Confirm +Discover → Schema → Action → Sign → Broadcast → Submit Hash → Poll until Confirmed ``` -Every Yield.xyz transaction follows this lifecycle. Here's each step in detail. +Every Yield.xyz transaction follows this lifecycle. Here's each step in detail. Two things +shape how you drive it: the action's `executionPattern` (synchronous / asynchronous / batch) +decides sequencing, and **polling is the only way to learn completion — there are no webhooks.** ## Step 1: Discover Yields @@ -41,14 +43,33 @@ const yield_ = await fetch(`https://api.yield.xyz/v1/yields/${yieldId}`, { headers: { "x-api-key": API_KEY }, }).then(r => r.json()); -// Check required arguments -const enterArgs = yield_.mechanics.arguments.enter; -console.log(enterArgs); -// { amount: "string (required)", validatorAddress: "string (required for staking)", ... } +// Check required arguments — `enter` is an OBJECT with a `fields` ARRAY, +// NOT a flat map. Each field describes one argument. +const enterFields = yield_.mechanics.arguments.enter.fields; +console.log(enterFields); +// [ +// { +// name: "amount", +// type: "string", +// label: "Amount", +// description: "Amount in human-readable token units...", +// required: true, +// isArray: false, +// minimum: "0", +// maximum: null, +// // staking yields may add e.g. { name: "validatorAddresses", isArray: true, enum: [...] } +// }, +// ... +// ] + +// Iterate fields to know which arguments to send: +for (const field of enterFields) { + console.log(`${field.name}: ${field.type}${field.isArray ? "[]" : ""} ${field.required ? "(required)" : "(optional)"}`); +} -// Check entry limits -console.log(yield_.entryLimits); -// { minimum: "0.01", maximum: "1000000" } +// Check entry limits — lives under `mechanics.entryLimits` and CAN BE null. +console.log(yield_.mechanics.entryLimits); +// { minimum: "0", maximum: null, subsequentMinimum: null } — or null entirely ``` ## Step 3: Call Action Endpoint @@ -101,7 +122,7 @@ interface ActionDto { intent: string; // "enter" | "exit" | "manage" type: string; // "STAKE" for enter, "UNSTAKE" for exit — // applies even to lending/vault yields (surprising naming) - executionPattern: string; // e.g. "synchronous" + executionPattern: string; // "synchronous" | "asynchronous" | "batch" — see "Branch on executionPattern" status: string; // "CREATED" initially transactions: TransactionDto[]; // ...plus yieldId, address, amount, amountRaw, amountUsd, rawArguments, createdAt, completedAt @@ -119,21 +140,69 @@ interface TransactionDto { type: string; // "APPROVAL", "SUPPLY", "STAKE", etc. hash: string | null; // null until broadcast stepIndex: number; // Execution order - gasEstimate: { // { amount, gasLimit, token } - amount: string; - gasLimit: string; - token: object; - }; - executionPattern: null; // null at the transaction level (it lives on the action) - unsignedTransaction: string; // THE TRANSACTION TO SIGN + gasEstimate: string; // JSON STRING — must be JSON.parse()'d (like unsignedTransaction). + // Parses to { amount, gasLimit, token }, e.g. + // '{"amount":"0.0000004","gasLimit":"38704","token":{...}}' + unsignedTransaction: string; // THE TRANSACTION TO SIGN — also a JSON string on EVM } ``` +> **`gasEstimate` is a JSON-encoded STRING, not an object.** Call `JSON.parse(tx.gasEstimate)` +> to read `amount` / `gasLimit` / `token` — exactly like `unsignedTransaction`. +> There is no transaction-level `executionPattern`; it lives only on the action (see below). + **Critical rules:** 1. Execute in `stepIndex` order (0, then 1, then 2...) -2. Wait for CONFIRMED status before proceeding to next +2. For `synchronous` actions, wait for CONFIRMED status before proceeding to next (see "Branch on executionPattern") 3. NEVER modify `unsignedTransaction` +## Branch on `action.executionPattern` + +How you sequence the transactions depends entirely on `action.executionPattern`. Do NOT +assume synchronous — async and batch yields break if you serialize them. There are +**three** values: + +| Pattern | What it means | How to drive it | +|---|---|---| +| `synchronous` | Each tx must confirm before the next is broadcast | Submit `stepIndex` 0, wait until Yield reports `CONFIRMED`, then submit `stepIndex` 1, and so on. | +| `asynchronous` | Order doesn't gate; txs are independent | Sign and broadcast all transactions without waiting between them. Still submit every hash. | +| `batch` | A single transaction containing multiple operations | Sign and broadcast the one transaction; there is no inter-step waiting. | + +```typescript +const txs = action.transactions.sort((a, b) => a.stepIndex - b.stepIndex); + +switch (action.executionPattern) { + case "synchronous": + // Submit one at a time; wait for Yield CONFIRMED before the next stepIndex. + for (const tx of txs) { + const hash = await signAndBroadcast(tx); + await submitHash(tx.id, hash); // step 6 + await waitForYieldStatus(tx.id, "CONFIRMED"); // see "Confirm and Monitor" — Yield status is authoritative + } + break; + + case "asynchronous": + // Broadcast all without waiting between them; still submit every hash. + await Promise.all(txs.map(async (tx) => { + const hash = await signAndBroadcast(tx); + await submitHash(tx.id, hash); + })); + break; + + case "batch": { + // Exactly one transaction bundling multiple operations. + const [tx] = txs; + const hash = await signAndBroadcast(tx); + await submitHash(tx.id, hash); + break; + } +} +``` + +> For `synchronous`, **Yield's status is the authoritative sequencing signal.** Submit the +> next `stepIndex` only after the prior tx reaches Yield status `CONFIRMED`. A non-TypeScript +> builder does not need to poll chain receipts itself — poll `GET /v1/transactions/{id}` (below). + ## Step 5: Sign the Transaction The format of `unsignedTransaction` depends on the chain. See the Chain Guide tool for specifics. @@ -158,11 +227,12 @@ for (const tx of action.transactions) { > **Use this section when signing in the browser with an injected wallet.** > The server-side example above does not work with MetaMask or Phantom EVM. -**Three required adjustments before sending to a browser wallet:** +**Required adjustments before sending to a browser wallet:** 1. `unsignedTransaction` is a JSON string — always `JSON.parse()` it first. 2. Strip `nonce`, `type`, and `chainId` — the wallet manages these itself. Keeping the API-returned values causes stale simulation and triggers "likely to fail" warnings or on-chain reverts. -3. Use `ethers.js BrowserProvider` — it automatically handles `gasLimit` → `gas` renaming and hex encoding. Do not pass the raw parsed object to `eth_sendTransaction` directly. +3. On **L2s** (Base, Arbitrum, Optimism, Polygon, …) also strip the gas fields (`gasLimit`, `maxFeePerGas`, `maxPriorityFeePerGas`) and let the wallet estimate — L2 gas moves fast and API values go stale. Keep them only on **Ethereum mainnet** (`chainId === 1`). (Consistent with `signing-patterns.md` and `common-pitfalls.md`.) +4. Use `ethers.js BrowserProvider` — it automatically handles `gasLimit` → `gas` renaming and hex encoding. Do not pass the raw parsed object to `eth_sendTransaction` directly. ```typescript import { BrowserProvider } from "ethers"; @@ -177,15 +247,22 @@ for (const tx of action.transactions.sort((a, b) => a.stepIndex - b.stepIndex)) // Strip fields the wallet manages — keeping them causes wrong simulation const { nonce, type, chainId, ...rest } = parsed; - const txToSend = { - to: rest.to, - data: rest.data, - value: rest.value ?? "0x0", - gasLimit: rest.gasLimit, // ethers.js renames this to gas automatically - maxFeePerGas: rest.maxFeePerGas, - maxPriorityFeePerGas: rest.maxPriorityFeePerGas, + const txToSend: Record = { + to: rest.to, + data: rest.data, + value: rest.value ?? "0x0", }; + // L2 gas consistency: on L2s, API gas values go stale fast — STRIP gas fields and + // let the wallet estimate. Keep them only on Ethereum mainnet (chainId === 1) for a + // better fee-estimate UX. (Matches signing-patterns.md and common-pitfalls.md.) + if (chainId === 1) { + txToSend.gasLimit = rest.gasLimit; // ethers.js renames this to gas automatically + txToSend.maxFeePerGas = rest.maxFeePerGas; + txToSend.maxPriorityFeePerGas = rest.maxPriorityFeePerGas; + } + // else (Base, Arbitrum, Optimism, Polygon, …): omit gas fields entirely. + let hash: string; try { const txResponse = await signer.sendTransaction(txToSend); @@ -234,6 +311,14 @@ function extractError(err: unknown): string { ## Step 6: Submit Hash (MANDATORY) +> **Two broadcast paths — pick exactly one per transaction, never both:** +> - `PUT /v1/transactions/{id}/submit-hash` — **you** broadcast the signed tx via your own +> RPC, then report the resulting hash to Yield (the path shown below). +> - `POST /v1/transactions/{id}/submit` — you hand Yield the **signed** transaction and Yield +> broadcasts it for you. Useful for backends without their own RPC. +> +> Both end with Yield tracking the transaction. Do not call both for the same tx. + After broadcasting each transaction, report the hash back to Yield.xyz: ```typescript @@ -266,13 +351,39 @@ The full set of transaction status values (10 total): Note it is `WAITING_FOR_SIGNATURE`, **not** `WAITING_FOR_SIGNING`. -Check status: +### Polling is the ONLY completion signal + +**Yield.xyz has no webhooks or callbacks — you learn completion by polling.** A single +status fetch is not enough: poll `GET /v1/transactions/{id}` every ~3–5s until the status +is **terminal** (`CONFIRMED`, `FAILED`, or `SKIPPED`), bounded by an overall timeout +(~2–5 min). For `synchronous` actions this is also your sequencing gate — only submit the +next `stepIndex` once the prior tx reaches `CONFIRMED`. + ```typescript -// Poll for confirmation -const status = await fetch( - `https://api.yield.xyz/v1/transactions/${tx.id}`, - { headers: { "x-api-key": API_KEY } } -).then(r => r.json()); +const TERMINAL = new Set(["CONFIRMED", "FAILED", "SKIPPED"]); + +async function waitForYieldStatus( + txId: string, + target: "CONFIRMED" = "CONFIRMED", + { intervalMs = 4_000, timeoutMs = 300_000 } = {}, +): Promise { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + const tx = await fetch( + `https://api.yield.xyz/v1/transactions/${txId}`, + { headers: { "x-api-key": API_KEY } }, + ).then((r) => r.json()); + + if (TERMINAL.has(tx.status)) { + if (tx.status !== target) { + throw new Error(`Transaction ${txId} reached terminal status ${tx.status}, expected ${target}`); + } + return tx.status; // CONFIRMED — safe to submit the next stepIndex (synchronous) + } + await new Promise((r) => setTimeout(r, intervalMs)); + } + throw new Error(`Timed out after ${timeoutMs}ms waiting for ${txId} to reach ${target}`); +} ``` ## Multi-Step Transaction Example @@ -290,23 +401,40 @@ const action = await sdk.api.enterYield({ // [0] APPROVAL (stepIndex: 0) — approve USDC spending // [1] SUPPLY (stepIndex: 1) — deposit into Aave +// This is a `synchronous` action (approve must confirm before supply). For async/batch, +// see "Branch on action.executionPattern". for (const tx of action.transactions.sort((a, b) => a.stepIndex - b.stepIndex)) { const parsed = JSON.parse(tx.unsignedTransaction); // Server-side: pass directly to your signer - // Browser wallet: strip nonce/type/chainId first — see "Browser Wallet Signing" section above + // Browser wallet: strip nonce/type/chainId (+ gas on L2) first — see "Browser Wallet Signing" const txResponse = await wallet.sendTransaction(parsed); - - // Server-side: .wait() is fine - // Browser wallet: use manual polling — .wait() hangs with injected providers const receipt = await txResponse.wait(); await sdk.api.submitTransactionHash(tx.id, { hash: receipt.hash }); - // MUST wait for CONFIRMED before executing the next stepIndex + // MUST wait for the next stepIndex until Yield reports CONFIRMED for THIS tx. + // Yield's status is the authoritative sequencing signal — poll it rather than + // relying on chain receipts (see "Polling is the ONLY completion signal"). + await waitForYieldStatus(tx.id, "CONFIRMED"); } ``` +## Idempotency & Retries + +Yield.xyz has **no idempotency keys.** Plan retries accordingly: + +- **`POST /v1/actions/enter` (exit/manage) is NOT idempotent.** Re-calling it mints a + **new** action with new transaction IDs. Dedupe client-side **before broadcasting** — + never blindly retry the action call and broadcast both. +- **`submit-hash` is idempotent for the same hash.** Re-sending `PUT .../submit-hash` with + the **same** hash for a tx is safe. Sending a **different** hash for an already-terminal + tx returns **HTTP 412** (precondition failed) — treat as "already settled," do not retry. +- **Recover in-flight state instead of re-creating it.** If you lose track of an action + (crash, timeout), fetch it rather than re-entering: + - `GET /v1/actions/{actionId}` — the action and its transactions (statuses, hashes). + - `GET /v1/actions?address=` — list recent actions for an address to reconcile. + ## Pending Actions After entering a position, check for pending actions (follow-up transactions). From ee1a8fa31f1d14700c7f215d7f58db2d47273e0d Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:25:17 +0200 Subject: [PATCH 07/23] refactor(builder-skill): make Yield-only, drop Perps/Borrow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MCP is a Yield MCP (no perps/borrow action/data tools — only api_spec can fetch their specs). Removing Perps/Borrow framing from the skill streamlines it and matches what the tools actually do. - SKILL.md: Yield-only description/scope; single api.yield.xyz base URL; remove the "which product" step (collapse to Yield use-case); drop perps/borrow spec examples and the submit-vs-submit-hash note; renumber workflow steps 0–5 - README.md: present as a Yield integration skill (drop the three-products framing) - mcp-tools.md: yield_get_api_spec presented as the Yield spec; drop perps-widget - dashboard-and-api-keys.md: features bullet no longer lists Perps/Borrow Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/README.md | 16 +-- .../skills/yield-agentkit-builder/SKILL.md | 120 ++++++------------ .../references/dashboard-and-api-keys.md | 2 +- .../references/mcp-tools.md | 23 +--- 4 files changed, 49 insertions(+), 112 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md index 87b2455..49b3d7a 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md @@ -3,9 +3,9 @@ [![AI Agent Skill](https://img.shields.io/badge/AI%20Agent-Skill-orange)](https://yield.xyz) [![Version](https://img.shields.io/badge/version-1.0.0-blue)](https://github.com/stakekit/agentkit) -> **Build on Yield.xyz with AI agents.** This skill teaches any MCP-compatible AI agent how to generate production-ready code that integrates with the Yield.xyz APIs across all three products: **Yield** (staking, lending, vaults, RWA across 80+ networks), **Perps** (perpetual futures on Hyperliquid), and **Borrow** (lending/borrowing markets). +> **Build on Yield.xyz with AI agents.** This skill teaches any MCP-compatible AI agent how to generate production-ready code that integrates with the Yield.xyz APIs — staking, lending, vaults, and RWA across 80+ networks. -The skill works alongside the [Yield.xyz AgentKit MCP server](https://mcp.yield.xyz/mcp), which provides live access to each product's OpenAPI spec and to public reference repos for looking up schemas, field definitions, and working code during generation. +The skill works alongside the [Yield.xyz AgentKit MCP server](https://mcp.yield.xyz/mcp), which provides live access to the OpenAPI spec and to public reference repos for looking up schemas, field definitions, and working code during generation. --- @@ -17,16 +17,12 @@ Once installed, the agent activates this skill when you ask to build something: - "Integrate USDC lending into my app" - "Generate a backend service that enters yield positions" - "Set up a neobank with DeFi yield features" -- "Integrate the Perps API for perpetual futures trading" -- "Add the perps widget to my site" -- "Wire the Borrow API into my backend" - "How do I sign Yield.xyz transactions with MetaMask?" -It starts by asking which product you're building on — **Yield**, **Perps**, or -**Borrow** — then recommends the best integration option (widget, SDK, or direct -REST API) for what you're building. From there it generates code that calls the -right product's REST API directly, using correct field names, proper transaction -signing, and the full submit-hash lifecycle. +It starts by understanding what you're building, then recommends the best +integration option (widget, SDK, or direct REST API) for it. From there it +generates code that calls the Yield.xyz REST API directly, using correct field +names, proper transaction signing, and the full submit-hash lifecycle. --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 93dcae2..a67a695 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -1,6 +1,6 @@ --- name: yield-agentkit-builder -description: Build applications that integrate with the Yield.xyz APIs. Detailed builder guidance in this skill is Yield-only — staking, lending, vaults, and RWA across 80+ networks. Perps (perpetual futures on Hyperliquid) and Borrow (lending/borrowing markets) are supported via the live spec (`yield_get_api_spec({ product })`) but use a different action model. Generates production-ready code covering REST API integration, transaction signing, wallet connection, and fee monetization. Use when user wants to build an app, integrate yield/perps/borrow, generate code, or set up a project using Yield.xyz. +description: Build applications that integrate with the Yield.xyz APIs — staking, lending, vaults, and RWA across 80+ networks. Generates production-ready code covering REST API integration, transaction signing, wallet connection, and fee monetization. Use when user wants to build an app, integrate yield, generate code, or set up a project using Yield.xyz. metadata: author: Yield.xyz version: "1.0.0" @@ -13,15 +13,8 @@ This skill helps developers build applications that integrate with the Yield.xyz It generates production-ready code, guides architecture decisions, and ensures correct API usage across all supported chains and protocols. -**Scope: this skill's detailed builder guidance is Yield-only** — staking, lending, -vaults, and RWA across 80+ networks. Perps and Borrow are still supported, but you -build them straight off the live spec — `yield_get_api_spec({ product: "perps" | "borrow" })` — -rather than from the worked patterns here, because they use a **different action model**: -a single `POST /v1/actions` with a `type` discriminator (not the Yield -`enter` / `exit` / `manage` endpoints), and they submit the signed transaction with -`POST /v1/transactions/{id}/submit` — **not** `submit-hash`. Everywhere below, the -detailed flows, reference files, and submit-hash lifecycle describe **Yield**; for -Perps/Borrow, lean on the live spec and keep those two differences in mind. +**Scope: this skill builds Yield integrations** — staking, lending, vaults, and RWA +across 80+ networks. --- @@ -75,17 +68,12 @@ the `yield-agentkit` skill — not this one. ### 1. API Base URL -Each product has its own production base URL: +The production base URL is `https://api.yield.xyz`, with the live OpenAPI spec at +`https://api.yield.xyz/docs.json`. -| Product | Base URL | Live spec | -|---|---|---| -| **Yield** | `https://api.yield.xyz` | `https://api.yield.xyz/docs.json` | -| **Perps** | `https://perps.yield.xyz` | `https://perps.yield.xyz/docs.json` | -| **Borrow** | `https://borrow.yield.xyz` | `https://borrow.yield.xyz/docs.json` | - -These are the only correct production URLs. Do not use `api.stakek.it`, +This is the only correct production URL. Do not use `api.stakek.it`, `api.stakekit.io`, or any other legacy domain. Every code sample, fetch call, -and SDK config must use the base URL for the product being integrated. +and SDK config must use this base URL. ### 2. API Key Requirement @@ -113,8 +101,8 @@ and execution/signing belongs to the dedicated skills below, not to a code-gener session. **For building, get data from the REST API instead.** Use the user's API key to call -the live REST endpoint directly (`https://api.yield.xyz/v1/...`, or the perps/borrow -base) via `curl`, `fetch`, or any HTTP client — that gives the full, unmodified +the live REST endpoint directly (`https://api.yield.xyz/v1/...`) via `curl`, `fetch`, +or any HTTP client — that gives the full, unmodified response to build against. The only MCP tools you use are the read-only **doc tools** (`yield_get_api_spec`, `yield_list_repos`, etc.). @@ -146,9 +134,8 @@ response to build against. The only MCP tools you use are the read-only **doc to The API evolves continuously. Do not rely on hardcoded field names or schemas from this skill's reference files. Instead: -1. **Fetch the live OpenAPI spec** for the product being integrated — `yield_get_api_spec({ product })`, - or the product's `docs.json` directly (`api.yield.xyz`, `perps.yield.xyz`, or - `borrow.yield.xyz`) — to discover current field names, types, and constraints +1. **Fetch the live OpenAPI spec** — `yield_get_api_spec`, or `api.yield.xyz/docs.json` + directly — to discover current field names, types, and constraints 2. **Call the actual API endpoint** with the user's key to see the real response shape 3. **Then generate code** based on what the live spec and actual responses show @@ -302,48 +289,18 @@ If registration fails, stop and surface the error to the user — do not attempt build anything until the MCP is connected. Full details and config snippets are in [`references/setup.md`](./references/setup.md). -### Step 1 — Ask Which Product (do this BEFORE anything else) - -**The first thing you ask the user is which Yield.xyz product they want to integrate.** -This single question removes most of the ambiguity from the rest of the session — it -determines the API base URL, which spec to fetch, and which integration options to -recommend. Don't assume "Yield" just because that's the default; ask. - -Yield.xyz has **three products**: - -| Product | What it gives the integrator | Integration options to offer | -|---|---|---| -| **Yield** | DeFi staking, lending, vaults, and RWA yields across 80+ networks | Widget (drop-in React component), TypeScript/JS SDK, or direct REST API integration | -| **Perps** | Perpetual futures trading (Hyperliquid integrated) | Perps REST API (integrate directly), or the **perps widget** (public reference repo — get the link via `yield_list_repos`) | -| **Borrow** | Lending/borrowing markets | Borrow REST API (integrate the endpoints into your existing backend/app) | - -Ask plainly, e.g.: - -> "Yield.xyz has three products you can build on: **Yield** (staking, lending, -> vaults, RWA yields), **Perps** (perpetual futures via Hyperliquid), and **Borrow** -> (lending/borrowing markets). Which one do you want to integrate?" - -Once they choose, recommend the integration option that best fits what they're -building (next step), and from then on use that product's base URL and spec -(`yield_get_api_spec({ product: "yield" | "perps" | "borrow" })`). - -### Step 2 — Understand the Use Case & Recommend an Approach +### Step 1 — Understand the Use Case & Recommend an Approach -Now ask what they're building. Combined with the product they chose, the answer -determines the integration option, architecture, signing approach, and which -reference files to load. +This skill builds **Yield** integrations (staking, lending, vaults, RWA), so there's +no product choice to make — go straight to understanding what the user is building. +Ask what they're building; the answer determines the integration option, architecture, +signing approach, and which reference files to load. -- **Yield** — recommend Widget for the fastest drop-in path, the SDK for - TypeScript/JS apps that want typed API access, or direct REST for everything - else (other languages, custom backends). Then map their product type to the - signing/architecture pattern below. -- **Perps** — recommend the perps widget for a fast self-custodial trading UI - (fetch the repo link with `yield_list_repos` and read its source), or the Perps - REST API for a custom integration. -- **Borrow** — recommend integrating the Borrow REST API endpoints into their - existing app/backend. +Recommend Widget for the fastest drop-in path, the SDK for TypeScript/JS apps that +want typed API access, or direct REST for everything else (other languages, custom +backends). Then map their product type to the signing/architecture pattern below. -For **Yield**, map the product type to architecture and signing: +Map the product type to architecture and signing: | Product Type | Signing | Key Reference | |---|---|---| @@ -359,31 +316,27 @@ architecture diagrams and patterns per product type. When the docs don't fully resolve an integration question — or you're stuck implementing one — use **`yield_list_repos`** to find the relevant public repo -(widget, perps widget, SDK, api-recipes, signers, shield, etc.) and read its raw -source as a working reference. +(widget, SDK, api-recipes, signers, shield, etc.) and read its raw source as a +working reference. -### Step 3 — Look Up the API Spec +### Step 2 — Look Up the API Spec -Before generating any code, fetch the live OpenAPI spec for the chosen product to -discover current field names and request/response shapes: +Before generating any code, fetch the live OpenAPI spec to discover current field +names and request/response shapes: -**Option A — Use the doc tool (pass the product):** +**Option A — Use the doc tool:** ``` -yield_get_api_spec({ product: "yield", endpoint: "/v1/actions/enter", section: "endpoints" }) -yield_get_api_spec({ product: "perps", query: "order" }) -yield_get_api_spec({ product: "borrow", query: "market" }) +yield_get_api_spec({ endpoint: "/v1/actions/enter", section: "endpoints" }) ``` -**Option B — Fetch directly with the user's key (use the product's spec URL):** +**Option B — Fetch directly with the user's key:** ```bash -curl https://api.yield.xyz/docs.json | jq '.paths["/v1/actions/enter"]' # Yield -curl https://perps.yield.xyz/docs.json | jq '.paths' # Perps -curl https://borrow.yield.xyz/docs.json| jq '.paths' # Borrow +curl https://api.yield.xyz/docs.json | jq '.paths["/v1/actions/enter"]' ``` Use the spec as the source of truth. Never assume field names from memory. -### Step 4 — Call the Real API to See Actual Responses +### Step 3 — Call the Real API to See Actual Responses Use the user's API key to make a real API call and inspect the response. This lets you see the actual field names, nesting, and data types in the response: @@ -395,22 +348,21 @@ curl -s "https://api.yield.xyz/v1/yields?network=base&token=USDC&limit=1" \ Use this real response as the reference when building the frontend or backend integration. -### Step 5 — Generate Code +### Step 4 — Generate Code Generate code that: -1. Uses the chosen product's base URL (`api.yield.xyz`, `perps.yield.xyz`, or `borrow.yield.xyz`) +1. Uses the base URL `api.yield.xyz` 2. Passes the user's API key via `x-api-key` header 3. Uses field names exactly as seen in the live spec and API responses -4. Handles the full transaction lifecycle. **For Yield, always report the hash after +4. Handles the full transaction lifecycle. **Always report the hash after broadcasting** via `PUT /v1/transactions/{txId}/submit-hash` (action -> sign -> - broadcast -> submit-hash). **Perps and Borrow do not use `submit-hash`** — they - submit the signed transaction with `POST /v1/transactions/{id}/submit` instead. + broadcast -> submit-hash). 5. Follows the signing pattern appropriate for the user's product type and wallet choice See **[`references/signing-patterns.md`](./references/signing-patterns.md)** for wallet SDK references and signing guidance per chain. -### Step 6 — Run the Project Yourself and Report URLs +### Step 5 — Run the Project Yourself and Report URLs **Do not tell the user "now run `npm run dev`" and walk away.** The skill's job isn't done until the project is actually running and you've handed the user the @@ -479,7 +431,7 @@ before generating code for that topic.** | **[`references/dashboard-and-api-keys.md`](./references/dashboard-and-api-keys.md)** | How the dashboard/API key controls which yields & features are enabled — read when a yield "isn't available" or returns `400 not enabled` | | **[`references/yield-types.md`](./references/yield-types.md)** | `GET /v1/yields` query params + the yield-type categories (high level; DTO is source of truth) | | **[`references/api-limits.md`](./references/api-limits.md)** | Rate limits, key tiers, throttling | -| **[`references/transaction-lifecycle.md`](./references/transaction-lifecycle.md)** | End-to-end **Yield** transaction flow: build → sign → broadcast → submit-hash → confirm. (Perps/Borrow use `POST /v1/transactions/{id}/submit` instead of `submit-hash`.) | +| **[`references/transaction-lifecycle.md`](./references/transaction-lifecycle.md)** | End-to-end transaction flow: build → sign → broadcast → submit-hash → confirm. | --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md index 93a7d5d..4110b95 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/dashboard-and-api-keys.md @@ -8,7 +8,7 @@ A large part of how the Yield.xyz API behaves for a given integration is **not** - The dashboard controls, per project/key: - **Which yields are enabled** — you turn individual yields (and whole networks/providers) on or off. - **Fees** — deposit, performance, and management fees are configured here, not passed in code. They're applied server-side to the actions the API builds for your key. - - **Which products/features are enabled** — Yield, Perps, Borrow, and specific capabilities can be toggled per key. + - **Which features are enabled** — specific Yield capabilities can be toggled per key. - **Rate-limit tier** and other account-level limits. ## The consequence builders must internalize diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md index 7f86516..8fa7318 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md @@ -35,17 +35,9 @@ The MCP exposes **five** live doc tools, all read-only and safe. Prefer them ove - **Use when:** You have a doc URL and need the authoritative spec content with exact field names, types, and examples. ### `yield_get_api_spec` -Fetches the **live** OpenAPI spec for whichever Yield.xyz product you're integrating. This is the **source of truth** for request bodies, field constraints (min/max/enum), error response shapes, and operationIds. Accepts a `product` arg, an optional `endpoint` filter (e.g. `/v1/actions/enter`, `/v1/markets/{marketId}`), an optional `query` keyword search across paths, and `method`/`section` filters. +Fetches the **live** Yield OpenAPI spec (`https://api.yield.xyz/docs.json`, default `product` `yield`) — covering staking, liquid/restaking, lending, vaults, and RWA. This is the **source of truth** for request bodies, field constraints (min/max/enum), error response shapes, and operationIds. Accepts an optional `endpoint` filter (e.g. `/v1/actions/enter`), an optional `query` keyword search across paths, and `method`/`section` filters. -The `product` arg selects which spec to fetch: - -| `product` | Spec URL | Covers | -|---|---|---| -| `yield` (default) | `https://api.yield.xyz/docs.json` | Staking, liquid/restaking, lending, vaults, RWA | -| `perps` | `https://perps.yield.xyz/docs.json` | Perpetual futures (Hyperliquid) | -| `borrow` | `https://borrow.yield.xyz/docs.json` | Lending/borrowing markets | - -**Use when:** Before generating any integration code, for any of the three products. Always verify current field names against this tool — don't trust memory. +**Use when:** Before generating any integration code. Always verify current field names against this tool — don't trust memory. > **Chain formats, transaction lifecycle, yield types, safety rules, and API limits used to be MCP doc tools (`yield_get_chain_guide`, `yield_get_transaction_guide`, `yield_get_yields_endpoint_guide`, `yield_get_safety_rules`, `yield_get_api_limits`).** They are no longer on the MCP — that guidance now lives in this skill's `references/` files. See [Static Guidance](#static-guidance--read-from-this-skills-reference-files). @@ -55,9 +47,9 @@ The `product` arg selects which spec to fetch: - **Use when:** user says "I'm getting a 422", "400 error", "rate limited", "transaction failed", "FAILED status", "why did this fail", or pastes any error code / message from the API. ### `yield_list_repos` -- Lists the public StakeKit/Yield.xyz GitHub repos that serve as **working code references** for integrations — the staking/yield widget (and its Next.js / Vite reference apps), the **perps widget** (`perps-widget`), the TypeScript/JS SDKs, runnable `api-recipes`, the `signers` and `shield` transaction-security libraries, and an `assets` repo for token/provider logos. Returns each repo with a one-line description and the raw-file fetch pattern (`https://raw.githubusercontent.com/stakekit///`). +- Lists the public StakeKit/Yield.xyz GitHub repos that serve as **working code references** for integrations — the staking/yield widget (and its Next.js / Vite reference apps), the TypeScript/JS SDKs, runnable `api-recipes`, the `signers` and `shield` transaction-security libraries, and an `assets` repo for token/provider logos. Returns each repo with a one-line description and the raw-file fetch pattern (`https://raw.githubusercontent.com/stakekit///`). -- **Use when:** the other doc tools don't fully resolve an integration problem and you need to read real, working source code — e.g. "how does the widget wire wallet connect", "show me a runnable enter-position example", "how do I integrate the perps widget", "how does Shield validate a transaction". It points at code; it never returns live data — use the REST API for that. +- **Use when:** the other doc tools don't fully resolve an integration problem and you need to read real, working source code — e.g. "how does the widget wire wallet connect", "show me a runnable enter-position example", "how does Shield validate a transaction". It points at code; it never returns live data — use the REST API for that. --- @@ -112,8 +104,8 @@ user's API key (`curl`, `fetch`, `axios`, …) and inspect the full JSON. | `get_transaction` | Poll a transaction's status (hash, broadcast timestamp) | `GET /v1/transactions/{transactionId}` | | `submit_hash` | Register a broadcast hash against unsigned transactions for status tracking | `PUT /v1/transactions/{transactionId}/submit-hash` | -> REST paths are shown relative to the product base URL (`https://api.yield.xyz` for -> Yield). Prepend the base for the product you're integrating. +> REST paths are shown relative to the base URL `https://api.yield.xyz`. Prepend the +> base when calling them. **Even for self-documenting schema** (e.g. `yields_get`), prefer `yield_get_api_spec` + a live REST call. The REST response carries the full `mechanics.arguments` schema; the @@ -128,9 +120,6 @@ When you need information during a builder session: **Live needs → MCP doc tools:** ``` Need API reference / schema? → yield_get_api_spec - (pass product: "perps" or "borrow" - when integrating those products; - default product is "yield") Need other endpoint schemas? → yield_get_api_spec({ endpoint: "..." }) Need a working code example / stuck implementing something? → yield_list_repos, then read the raw source From e9e494f3b23c9576d7816ea242ceb12ef27e4d9f Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:31:55 +0200 Subject: [PATCH 08/23] docs(builder-skill): widget with your own signing infra (externalProviders) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the @stakekit/widget `externalProviders` prop — how to plug in your own address + signer (custody/HSM, embedded/agent wallet, host-app wallet) and skip the built-in connect-wallet flow. Grounded in the widget repo's SKExternalProviders/SKWallet types: provider implements signMessage/switchChain/sendTransaction(skTx, txMeta) — your infra signs+broadcasts and returns the hash; the widget handles action lifecycle and submit-hash. Also note disableInjectedProviderDiscovery, validatorsConfig, mapWalletListFn, and the React Native @stakekit/use-inject-provider + WebView path. Soften the widget "less customization" tradeoff in setup.md (BYO wallet IS supported) and point to the new section. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../references/integration-patterns.md | 57 +++++++++++++++++++ .../references/setup.md | 9 ++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md index 93a1317..1ff191c 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md @@ -74,6 +74,63 @@ The React component is `SKApp` (requires React 19+). For non-React apps use the build (`renderSKWidget` from `@stakekit/widget/bundle`). Refer to the widget docs and the [widget repo](https://github.com/stakekit/widget) for installation and configuration. +#### Bring your own signing infra (skip connect-wallet) + +By default `SKApp` renders its own connect-wallet UI (wagmi/RainbowKit connectors). +**If you already have the user's address and your own signer** — a custody/HSM backend, +an embedded or agent wallet (Privy/MoonPay/etc.), or a host app that manages the wallet — +pass the **`externalProviders`** prop. This **skips the connection step entirely**: the +widget uses the address and signer you supply instead of asking the user to connect. + +```tsx +import "@stakekit/widget/package/css"; +import { SKApp, darkTheme } from "@stakekit/widget"; + + mySigner.signMessage(message), // -> signature string + switchChain: async (chainId) => { /* point your signer at chainId */ }, + // optional: let the widget poll for a receipt + getTransactionReceipt: async (txHash) => ({ transactionHash: txHash }), + // the widget hands you a DECODED tx + metadata; you sign AND broadcast, + // then return the on-chain hash. + sendTransaction: async (skTx, txMeta) => { + // skTx is a discriminated union by chain: { type: "evm" | "solana" | "ton" | "tron", tx } + // txMeta carries { txId, txType, actionId, actionType, amount, inputToken, providersDetails } + const txHash = await myInfra.signAndBroadcast(skTx); + return { type: "success", txHash }; // or { type: "error", error } or just the hash string + }, + }, + }} +/>; +``` + +Key points for this path: +- `externalProviders.provider` is the `SKWallet` interface: `signMessage`, `switchChain`, + optional `getTransactionReceipt`, and `sendTransaction(skTx, txMeta)`. **Your code does the + signing and broadcasting** inside `sendTransaction` and returns the hash — the widget then + handles the submit-hash/tracking for you. You never hand keys to the widget. +- `currentAddress` is the address the widget operates on; update it (re-render with a new + value) when your wallet switches accounts. +- `skTx` is already decoded per chain (`evm`/`solana`/`ton`/`tron`) — sign it as-is; don't + mutate it (same rule as raw `unsignedTransaction`). +- Related props: `disableInjectedProviderDiscovery` (stop the widget probing for injected + wallets when you supply your own), `validatorsConfig` (allow/block/prefer validators per + chain), and `mapWalletListFn`/`mapWalletFn` (customize the connector list *if* you do use + the built-in connect flow). +- **React Native / WebView host:** use `@stakekit/use-inject-provider` — pass your + wallet's EIP-1193 provider + the WebView ref to `useInjectProvider`, and it returns + `injectedJavaScript` + `onMessage` for the `react-native-webview` `WebView`. This also + skips the connection step. + ### SDK Integration (More Control) ```typescript const yields = await sdk.api.getYields({ network: "ethereum", token: "ETH" }); diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md index 89d34d0..3191eeb 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md @@ -128,10 +128,13 @@ transaction signing, and position tracking. - **Best for:** Consumer wallets, quick prototypes, MVPs, and any product that wants yield functionality without custom UI. -- **Tradeoffs:** Less customization. You get the StakeKit UI. Good for speed, less - control over look and feel. +- **Tradeoffs:** Less control over look and feel — you get the StakeKit UI. But you do + NOT have to use its connect-wallet flow: pass the **`externalProviders`** prop to plug + in your **own address + signing infra** (custody/HSM, embedded or agent wallet, host-app + wallet) and skip the connection step. See "Bring your own signing infra" in + `integration-patterns.md`. - **Match signals:** widget, drop-in, component, React component, quick start, fastest, - prototype, MVP. + prototype, MVP, embed yield UI with my own wallet/signer. - **Get started:** ```tsx From 56f25f0ed640452a1cd085503c729a2860c746f7 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:34:43 +0200 Subject: [PATCH 09/23] =?UTF-8?q?docs(builder-skill):=20debrand=20?= =?UTF-8?q?=E2=80=94=20Yield.xyz=20UI,=20not=20StakeKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Company name is Yield.xyz. "the StakeKit UI" → "the prebuilt Yield.xyz UI". The literal @stakekit/* npm packages and github.com/stakekit/* URLs are real identifiers, kept as-is. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/references/setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md index 3191eeb..e1d96ad 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md @@ -128,7 +128,7 @@ transaction signing, and position tracking. - **Best for:** Consumer wallets, quick prototypes, MVPs, and any product that wants yield functionality without custom UI. -- **Tradeoffs:** Less control over look and feel — you get the StakeKit UI. But you do +- **Tradeoffs:** Less control over look and feel — you get the prebuilt Yield.xyz UI. But you do NOT have to use its connect-wallet flow: pass the **`externalProviders`** prop to plug in your **own address + signing infra** (custody/HSM, embedded or agent wallet, host-app wallet) and skip the connection step. See "Bring your own signing infra" in From a511924bef22544a5d0cf3a4e2e5bad793cc1786 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:40:34 +0200 Subject: [PATCH 10/23] docs(builder-skill): add npm + GitHub links for packages/tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make recommended packages and tools clickable (verified live before adding): - @stakekit/widget → npm + repo (was unlinked across 17 mentions) - @stakekit/use-inject-provider → npm (React Native section) - @yieldxyz/sdk → npm link in SKILL.md SDK option - consolidated "Packages & resources" list in setup.md (packages, repos, docs, dashboard, live spec) - fix dead @solana/web3.js doc link (404) → npm Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/SKILL.md | 3 ++- .../references/integration-patterns.md | 5 ++-- .../references/setup.md | 27 ++++++++++++++++++- .../references/signing-patterns.md | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index a67a695..0f4da5c 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -447,7 +447,8 @@ sdk.configure({ apiKey: process.env.YIELD_API_KEY }); const yields = await sdk.api.getYields({ network: "ethereum" }); ``` -Source: [github.com/stakekit/sdk](https://github.com/stakekit/sdk) (published as `@yieldxyz/sdk`). +Source: [github.com/stakekit/sdk](https://github.com/stakekit/sdk) (published on +[npm as `@yieldxyz/sdk`](https://www.npmjs.com/package/@yieldxyz/sdk)). For other languages (Python, Go, Rust), or any non-TypeScript agent, skip the SDK and call the REST API directly — every endpoint is plain HTTP. Refer to diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md index 1ff191c..7687379 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md @@ -71,7 +71,8 @@ function YieldPage() { ``` The React component is `SKApp` (requires React 19+). For non-React apps use the bundled -build (`renderSKWidget` from `@stakekit/widget/bundle`). Refer to the widget docs and the +build (`renderSKWidget` from `@stakekit/widget/bundle`). Refer to the widget docs, the +[widget on npm](https://www.npmjs.com/package/@stakekit/widget), and the [widget repo](https://github.com/stakekit/widget) for installation and configuration. #### Bring your own signing infra (skip connect-wallet) @@ -126,7 +127,7 @@ Key points for this path: wallets when you supply your own), `validatorsConfig` (allow/block/prefer validators per chain), and `mapWalletListFn`/`mapWalletFn` (customize the connector list *if* you do use the built-in connect flow). -- **React Native / WebView host:** use `@stakekit/use-inject-provider` — pass your +- **React Native / WebView host:** use [`@stakekit/use-inject-provider`](https://www.npmjs.com/package/@stakekit/use-inject-provider) — pass your wallet's EIP-1193 provider + the WebView ref to `useInjectProvider`, and it returns `injectedJavaScript` + `onMessage` for the `react-native-webview` `WebView`. This also skips the connection step. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md index e1d96ad..264076c 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md @@ -152,8 +152,9 @@ The React component is `SKApp`. For a non-React app, use the bundled build: `import { renderSKWidget } from "@stakekit/widget/bundle"`. - **Resources:** - - [Widget Documentation](https://docs.yield.xyz/docs/widget) + - [Widget on npm](https://www.npmjs.com/package/@stakekit/widget) - [widget repo](https://github.com/stakekit/widget) + - [Widget Documentation](https://docs.yield.xyz/docs/widget) ### TypeScript SDK (`@yieldxyz/sdk`) — Recommended Default @@ -254,3 +255,27 @@ configuration. Combined with the REST API for transactions. - [Programmatic Access Guide](https://docs.yield.xyz/docs/programmatic-access-guide) - [Fees Overview](https://docs.yield.xyz/docs/fees) - [Allocator Vaults](https://docs.yield.xyz/docs/allocator-vaults-oavs-introduction) + +--- + +## Packages & resources + +A single index of everything this skill recommends, with links. + +**npm packages** +- `@yieldxyz/sdk` — [npm](https://www.npmjs.com/package/@yieldxyz/sdk) +- `@stakekit/widget` — [npm](https://www.npmjs.com/package/@stakekit/widget) +- `@stakekit/use-inject-provider` (React Native / WebView host) — [npm](https://www.npmjs.com/package/@stakekit/use-inject-provider) + +**Repos** +- SDK — [github.com/stakekit/sdk](https://github.com/stakekit/sdk) +- Widget — [github.com/stakekit/widget](https://github.com/stakekit/widget) +- API recipes (runnable REST + ethers.js examples) — [github.com/stakekit/api-recipes](https://github.com/stakekit/api-recipes) +- Shield (transaction-security validation) — [github.com/stakekit/shield](https://github.com/stakekit/shield) +- Signers (signing helpers) — [github.com/stakekit/signers](https://github.com/stakekit/signers) + +**Docs & tooling** +- Documentation — [docs.yield.xyz](https://docs.yield.xyz) +- API reference — [getting started](https://docs.yield.xyz/reference/getting-started-with-your-api) +- Dashboard (get an API key) — [dashboard.yield.xyz](https://dashboard.yield.xyz) +- Live OpenAPI spec — [api.yield.xyz/docs.json](https://api.yield.xyz/docs.json) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index 0352845..244b787 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -170,7 +170,7 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | Library | Use Case | Docs | |---|---|---| -| **@solana/web3.js v2** | General-purpose Solana (new version) | https://solana.com/docs/clients/javascript | +| **@solana/web3.js v2** | General-purpose Solana (new version) | https://www.npmjs.com/package/@solana/web3.js | | **@solana/web3.js v1** | Legacy projects | https://solana-labs.github.io/solana-web3.js/ | | **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.app/solana/integrating/extension | | **Backpack** | Browser wallet (multi-chain, popular in Solana) | https://docs.backpack.app | From 498124a50026d94a163618b873d4d054b820365a Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:45:48 +0200 Subject: [PATCH 11/23] docs(builder-skill): fix dead third-party wallet/lib doc links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link-check turned up 5 stale URLs in the signing tables (all verified replacements): - Phantom docs.phantom.app/... (404) → docs.phantom.com - WalletConnect Web3Modal docs.walletconnect.com/web3modal (404) → docs.reown.com/appkit/overview (rebrand) - Coinbase docs.cloud.coinbase.com/... (404) → github.com/coinbase/coinbase-wallet-sdk - @solana/web3.js v1 solana-labs.github.io (404) → github.com/solana-labs/solana-web3.js - CosmosKit cosmoskit.com (dead) → github.com/cosmology-tech/cosmos-kit All Yield.xyz npm packages and github.com/stakekit/* repo links verified 200. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../references/signing-patterns.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index 244b787..95d3311 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -127,10 +127,10 @@ For apps where the user signs in the browser with an injected wallet. | Wallet | SDK / Integration | Docs | |---|---|---| | **MetaMask** | MetaMask SDK (`@metamask/sdk`) | https://docs.metamask.io/wallet/connect/ | -| **Phantom (EVM)** | Phantom provider (`window.phantom.ethereum`) | https://docs.phantom.app/solana/integrating/extension | -| **WalletConnect** | WalletConnect Web3Modal (`@web3modal/ethers` or `@web3modal/wagmi`) | https://docs.walletconnect.com/web3modal/about | +| **Phantom (EVM)** | Phantom provider (`window.phantom.ethereum`) | https://docs.phantom.com | +| **WalletConnect** | WalletConnect Web3Modal (`@web3modal/ethers` or `@web3modal/wagmi`) | https://docs.reown.com/appkit/overview | | **Rainbow** | RainbowKit (`@rainbow-me/rainbowkit`) | https://www.rainbowkit.com/docs/introduction | -| **Coinbase Wallet** | Coinbase Wallet SDK (`@coinbase/wallet-sdk`) | https://docs.cloud.coinbase.com/wallet-sdk/docs | +| **Coinbase Wallet** | Coinbase Wallet SDK (`@coinbase/wallet-sdk`) | https://github.com/coinbase/coinbase-wallet-sdk | ### Multi-Wallet Abstraction @@ -139,7 +139,7 @@ For supporting multiple wallets with a single integration: | Library | What It Does | Docs | |---|---|---| | **wagmi + viem** | React hooks for wallet connection, signing, and state | https://wagmi.sh | -| **Web3Modal** | Drop-in modal supporting 300+ wallets via WalletConnect | https://docs.walletconnect.com/web3modal/about | +| **Web3Modal** | Drop-in modal supporting 300+ wallets via WalletConnect | https://docs.reown.com/appkit/overview | | **RainbowKit** | Polished wallet connection UI for React apps | https://www.rainbowkit.com/docs/introduction | | **ConnectKit** | Wallet connection modal by Family | https://docs.family.co/connectkit | @@ -171,8 +171,8 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | Library | Use Case | Docs | |---|---|---| | **@solana/web3.js v2** | General-purpose Solana (new version) | https://www.npmjs.com/package/@solana/web3.js | -| **@solana/web3.js v1** | Legacy projects | https://solana-labs.github.io/solana-web3.js/ | -| **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.app/solana/integrating/extension | +| **@solana/web3.js v1** | Legacy projects | https://github.com/solana-labs/solana-web3.js | +| **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.com | | **Backpack** | Browser wallet (multi-chain, popular in Solana) | https://docs.backpack.app | | **Solana Wallet Adapter** | Multi-wallet abstraction for React | https://github.com/anza-xyz/wallet-adapter | @@ -194,7 +194,7 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | **@cosmjs/proto-signing** | Protobuf-based signing | https://cosmos.github.io/cosmjs/ | | **Keplr** | Browser wallet (most popular Cosmos wallet) | https://docs.keplr.app/api/ | | **Leap** | Browser wallet (multi-chain Cosmos) | https://docs.leapwallet.io | -| **CosmosKit** | Multi-wallet abstraction for React | https://cosmoskit.com | +| **CosmosKit** | Multi-wallet abstraction for React | https://github.com/cosmology-tech/cosmos-kit | ### Cosmos-Specific Requirement Cosmos yields require the user's **public key** as an argument when calling the action From 4e2eea945f7cc13cd67095567ad0d253819026d3 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:53:31 +0200 Subject: [PATCH 12/23] =?UTF-8?q?docs(builder-skill):=20connect=20the=20fu?= =?UTF-8?q?ll=20flow=20=E2=80=94=20discover=20=E2=86=92=20=E2=80=A6=20?= =?UTF-8?q?=E2=86=92=20balances=20=E2=86=92=20pending=20=E2=86=92=20manage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verified against docs.yield.xyz, the live spec, and the official api-recipes Yields example. Make the end-to-end loop correct and connected: - distinguish ActionDto.status (CANCELED/CREATED/WAITING_FOR_NEXT/PROCESSING/FAILED/ SUCCESS/STALE — completes at SUCCESS, never "CONFIRMED") from TransactionDto.status (poll transactions for CONFIRMED/FAILED/SKIPPED) — these were conflated - close the pending-action loop: POST /v1/actions/manage with action=pendingAction.type, passthrough=pendingAction.passthrough (verbatim), arguments only if the pending action carries a schema → its transactions[] re-enter the sign → submit-hash → poll loop - fix PendingActionDto shape: { intent, type, passthrough, arguments?, amount? } — no `description` field (use type/amount) - add BalanceDto + BalanceType (active/entering/exiting/withdrawable/claimable/locked; only active earns); batch item shape includes outputTokenBalance; balances max 25 queries - connect discovery as the lifecycle entry (GET /v1/yields paginated → GET /v1/yields/{id} schema); point to api-recipes for a full runnable example - api-field-mapping: single-yield balances is POST (canonical), GET also works Co-Authored-By: Claude Opus 4.8 (1M context) --- .../references/api-field-mapping.md | 2 +- .../references/transaction-lifecycle.md | 158 +++++++++++++++--- 2 files changed, 136 insertions(+), 24 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md index 3d0d802..97be1f3 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md @@ -92,7 +92,7 @@ Additional live endpoints, useful for discovery and analytics. History endpoints | `/v1/providers` | GET | List protocol/validator providers | | `/v1/providers/{id}` | GET | Single provider metadata | | `/v1/yields/{id}/campaigns` | GET | Reward campaigns for a yield | -| `/v1/yields/{id}/balances` | GET | Balances for a single yield (query: `address`) | +| `/v1/yields/{id}/balances` | POST | Balances for a single yield. Body: `{ "address": "…" }`. **POST is the canonical/spec method (recommended).** `GET` with `?address=` also works today, but prefer POST | | `/v1/yields/{id}/balances/history` | GET | Balance history (indexed yields only) | | `/v1/yields/{id}/rewards/history` | GET | Reward history (indexed yields only) | | `/v1/yields/{id}/reward-rate/history` | GET | Reward-rate history | diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md index 3917425..99d0ee4 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md @@ -5,33 +5,51 @@ Complete step-by-step guide for executing transactions through the Yield.xyz API ## Overview ``` -Discover → Schema → Action → Sign → Broadcast → Submit Hash → Poll until Confirmed +Discover → Schema → Action → Sign → Broadcast → Submit Hash → Poll → Balances → Pending Action → Manage (→ Exit) ``` -Every Yield.xyz transaction follows this lifecycle. Here's each step in detail. Two things -shape how you drive it: the action's `executionPattern` (synchronous / asynchronous / batch) -decides sequencing, and **polling is the only way to learn completion — there are no webhooks.** +This is the **end-to-end loop**, and it closes: you enter a position, it appears in balances, +a follow-up surfaces as a per-balance `pendingAction`, you complete it via `POST /v1/actions/manage`, +and that manage action runs the *same* sign → submit → poll loop. Exiting works the same way +via `POST /v1/actions/exit`. Here's each step in detail. Two things shape how you drive it: +the action's `executionPattern` (synchronous / asynchronous / batch) decides sequencing, and +**polling is the only way to learn completion — there are no webhooks.** + +> **Action status vs. transaction status are two different enums — don't conflate them.** +> You poll **transaction** status (`GET /v1/transactions/{id}`) to a terminal +> `CONFIRMED`/`FAILED`/`SKIPPED` — that is the per-tx sequencing gate. The **action** +> (the wrapper) completes at `SUCCESS` or `FAILED` — an action never reaches "CONFIRMED". +> See "Step 4" and "Step 7". + +> **A full runnable end-to-end example** (discover → enter → balances → pending-action manage +> → exit) lives in the **api-recipes repo**: https://github.com/stakekit/api-recipes ## Step 1: Discover Yields Find available yield opportunities: ```typescript -// REST API -const response = await fetch("https://api.yield.xyz/v1/yields?network=ethereum&token=USDC", { - headers: { "x-api-key": API_KEY }, -}); +// REST API — filter + sort; paginate with limit/offset (max limit 100, see yield-types.md) +const response = await fetch( + "https://api.yield.xyz/v1/yields?network=ethereum&token=USDC&type=staking&sort=rewardRateDesc&limit=100&offset=0", + { headers: { "x-api-key": API_KEY } }, +); const yields = await response.json(); // SDK const yields = await sdk.api.getYields({ network: "ethereum", token: "USDC" }); ``` -Filter results by: +Filter / sort results by: - `network` — blockchain network - `token` — token symbol +- `type` — yield type (e.g. `staking`, `lending`, `vault`) +- `sort` — e.g. `rewardRateDesc` +- `limit` / `offset` — pagination (max `limit` 100; see yield-types.md) - `status.enter` — only yields accepting new deposits -- `apy` — annual percentage yield + +This `GET /v1/yields` discovery query is the opening of the lifecycle — pick a yield `id` +from the results, then read its schema (Step 2) before building any action. ## Step 2: Read the Schema @@ -123,12 +141,20 @@ interface ActionDto { type: string; // "STAKE" for enter, "UNSTAKE" for exit — // applies even to lending/vault yields (surprising naming) executionPattern: string; // "synchronous" | "asynchronous" | "batch" — see "Branch on executionPattern" - status: string; // "CREATED" initially + status: string; // ActionDto.status — NOT the transaction enum. One of: + // CANCELED, CREATED, WAITING_FOR_NEXT, PROCESSING, FAILED, SUCCESS, STALE. + // "CREATED" initially; completes at SUCCESS (or FAILED). Never "CONFIRMED". transactions: TransactionDto[]; // ...plus yieldId, address, amount, amountRaw, amountUsd, rawArguments, createdAt, completedAt } ``` +> **Action status ≠ transaction status.** The `ActionDto.status` enum is +> `CANCELED, CREATED, WAITING_FOR_NEXT, PROCESSING, FAILED, SUCCESS, STALE`. The +> `TransactionDto.status` enum (below) is a separate set ending in `CONFIRMED`/`FAILED`/`SKIPPED`. +> You poll **transaction** status to gate per-tx sequencing; the **action** is done when it +> reaches `SUCCESS` (or `FAILED`). An action never reaches "CONFIRMED". + Each entry of `action.transactions` is a `TransactionDto`: ```typescript @@ -435,21 +461,27 @@ Yield.xyz has **no idempotency keys.** Plan retries accordingly: - `GET /v1/actions/{actionId}` — the action and its transactions (statuses, hashes). - `GET /v1/actions?address=` — list recent actions for an address to reconcile. -## Pending Actions +## Step 8: Read Balances + +After entering, the position shows up in balances. This is also where follow-up actions surface. -After entering a position, check for pending actions (follow-up transactions). +**Batch (recommended)** — `POST /v1/yields/balances`, body `{ queries: [...] }`: +- Each query is `{ network, address, yieldId? }`. `network` and `address` are **required**; `yieldId` is optional (omit to get all of an address's positions on that network). +- **Max 25 queries** per request. -Query balances via `POST /v1/yields/balances` with a `queries` array. The response is -`{ items, errors }`, where each `items[]` entry is `{ yieldId, balances, rewardRate }`. -`pendingActions` is nested **per-balance** inside `items[].balances[]` — not at the top level. +**Single yield** — `POST /v1/yields/{yieldId}/balances`, body `{ address }`. + +The batch response is `{ items, errors }`, where each `items[]` entry is +`{ yieldId, balances, rewardRate, outputTokenBalance }`. `pendingActions` is nested +**per-balance** inside `items[].balances[]` — not at the top level. ```typescript -// REST API +// REST API — batch (max 25 queries; network + address required per query) const res = await fetch("https://api.yield.xyz/v1/yields/balances", { method: "POST", headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ - queries: [{ network: "base", address: walletAddress }], + queries: [{ network: "base", address: walletAddress }], // optional: yieldId }), }).then(r => r.json()); @@ -458,21 +490,101 @@ const res = await fetch("https://api.yield.xyz/v1/yields/balances", { // items: [ // { // yieldId: "base-usdc-aave-v3-lending", -// balances: [{ /* ...balance fields... */, pendingActions: [...] }], +// balances: [{ /* ...BalanceDto fields... */, pendingActions: [...] }], // rewardRate: { /* ... */ }, +// outputTokenBalance: { /* ... */ }, // }, // ], // errors: [], // } +``` + +### `BalanceDto` and `BalanceType` +Each `item.balances[]` entry is a `BalanceDto`. A position is split into multiple balance +rows by **type** (`BalanceType` enum): + +`active`, `entering`, `exiting`, `withdrawable`, `claimable`, `locked`. + +Only `active` earns — check the `isEarning` flag rather than assuming. + +```typescript +interface BalanceDto { + address: string; + type: string; // BalanceType — active | entering | exiting | withdrawable | claimable | locked + amount: string; // human-readable + amountRaw: string; // base units + amountUsd: string; + token: TokenDto; + pendingActions: PendingActionDto[]; // per-balance follow-ups (see Step 9) + isEarning: boolean; // true only for the earning portion (typically `active`) + // staking yields: one of these is present + validator?: ValidatorDto; + validators?: ValidatorDto[]; + // ERC-4626 vaults: share accounting + shareAmount?: string; + shareToken?: TokenDto; +} +``` + +## Step 9: Pending Actions → Manage (closing the loop) + +A `pendingAction` is a follow-up the position requires — e.g. claim matured rewards, complete +a withdrawal, restake. Each is a `PendingActionDto`: + +```typescript +interface PendingActionDto { + intent: string; // "manage" + type: string; // the action to run — e.g. CLAIM_UNSTAKED, WITHDRAW, RESTAKE_REWARDS, REDELEGATE + passthrough: string; // opaque server blob — pass back VERBATIM, never construct or edit it + arguments?: FieldsSchema | null; // present only when the action needs extra input (e.g. REDELEGATE → validatorAddress) + amount?: string; +} +``` + +There is **no `description` field** on a pending action (that lives on `TransactionDto`). +Print `pending.type` / `pending.amount`, not `pending.description`. + +**Execute a pending action** by posting it to `/v1/actions/manage` — map the pending action's +own fields straight across: + +```typescript for (const item of res.items) { for (const balance of item.balances) { - if (balance.pendingActions?.length > 0) { - // User needs to complete these (e.g., claim rewards) - for (const pending of balance.pendingActions) { - console.log(`Pending: ${pending.type} — ${pending.description}`); + for (const pending of balance.pendingActions ?? []) { + console.log(`Pending: ${pending.type}${pending.amount ? ` (${pending.amount})` : ""}`); + + const body: Record = { + yieldId: item.yieldId, + address: walletAddress, + action: pending.type, // e.g. CLAIM_UNSTAKED, WITHDRAW, RESTAKE_REWARDS + passthrough: pending.passthrough, // opaque blob — pass VERBATIM + }; + // Only send `arguments` if the pending action defines a non-null fields schema + // (e.g. REDELEGATE needs a validatorAddress). Read pending.arguments.fields the + // same way as enter fields (Step 2) to know what to supply. + if (pending.arguments != null) { + body.arguments = { /* values for pending.arguments.fields */ }; } + + const manageAction = await fetch("https://api.yield.xyz/v1/actions/manage", { + method: "POST", + headers: { "x-api-key": API_KEY, "Content-Type": "application/json" }, + body: JSON.stringify(body), + }).then(r => r.json()); + + // manageAction.transactions[] → run the SAME loop as enter: + // branch on manageAction.executionPattern → sign → submit-hash → poll transaction status. + // (See "Branch on action.executionPattern", Step 5, Step 6, Step 7.) } } } ``` + +**The full lifecycle, stated once:** enter → position appears in balances → a `pendingAction` +surfaces per-balance → `POST /v1/actions/manage` (with `action: pending.type` + verbatim +`pending.passthrough`, plus `arguments` only when `pending.arguments` is non-null) → that +manage action returns its own `transactions[]` → branch on its `executionPattern`, sign, +submit-hash, and poll transaction status to terminal. Exiting a position is the same flow via +`POST /v1/actions/exit`. A complete runnable example is in the +[api-recipes repo](https://github.com/stakekit/api-recipes). From 8456bf50b2760f2931b81f03403c65482350520a Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:55:56 +0200 Subject: [PATCH 13/23] Revert "docs(builder-skill): fix dead third-party wallet/lib doc links" This reverts commit 498124a50026d94a163618b873d4d054b820365a. --- .../references/signing-patterns.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index 95d3311..244b787 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -127,10 +127,10 @@ For apps where the user signs in the browser with an injected wallet. | Wallet | SDK / Integration | Docs | |---|---|---| | **MetaMask** | MetaMask SDK (`@metamask/sdk`) | https://docs.metamask.io/wallet/connect/ | -| **Phantom (EVM)** | Phantom provider (`window.phantom.ethereum`) | https://docs.phantom.com | -| **WalletConnect** | WalletConnect Web3Modal (`@web3modal/ethers` or `@web3modal/wagmi`) | https://docs.reown.com/appkit/overview | +| **Phantom (EVM)** | Phantom provider (`window.phantom.ethereum`) | https://docs.phantom.app/solana/integrating/extension | +| **WalletConnect** | WalletConnect Web3Modal (`@web3modal/ethers` or `@web3modal/wagmi`) | https://docs.walletconnect.com/web3modal/about | | **Rainbow** | RainbowKit (`@rainbow-me/rainbowkit`) | https://www.rainbowkit.com/docs/introduction | -| **Coinbase Wallet** | Coinbase Wallet SDK (`@coinbase/wallet-sdk`) | https://github.com/coinbase/coinbase-wallet-sdk | +| **Coinbase Wallet** | Coinbase Wallet SDK (`@coinbase/wallet-sdk`) | https://docs.cloud.coinbase.com/wallet-sdk/docs | ### Multi-Wallet Abstraction @@ -139,7 +139,7 @@ For supporting multiple wallets with a single integration: | Library | What It Does | Docs | |---|---|---| | **wagmi + viem** | React hooks for wallet connection, signing, and state | https://wagmi.sh | -| **Web3Modal** | Drop-in modal supporting 300+ wallets via WalletConnect | https://docs.reown.com/appkit/overview | +| **Web3Modal** | Drop-in modal supporting 300+ wallets via WalletConnect | https://docs.walletconnect.com/web3modal/about | | **RainbowKit** | Polished wallet connection UI for React apps | https://www.rainbowkit.com/docs/introduction | | **ConnectKit** | Wallet connection modal by Family | https://docs.family.co/connectkit | @@ -171,8 +171,8 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | Library | Use Case | Docs | |---|---|---| | **@solana/web3.js v2** | General-purpose Solana (new version) | https://www.npmjs.com/package/@solana/web3.js | -| **@solana/web3.js v1** | Legacy projects | https://github.com/solana-labs/solana-web3.js | -| **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.com | +| **@solana/web3.js v1** | Legacy projects | https://solana-labs.github.io/solana-web3.js/ | +| **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.app/solana/integrating/extension | | **Backpack** | Browser wallet (multi-chain, popular in Solana) | https://docs.backpack.app | | **Solana Wallet Adapter** | Multi-wallet abstraction for React | https://github.com/anza-xyz/wallet-adapter | @@ -194,7 +194,7 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | **@cosmjs/proto-signing** | Protobuf-based signing | https://cosmos.github.io/cosmjs/ | | **Keplr** | Browser wallet (most popular Cosmos wallet) | https://docs.keplr.app/api/ | | **Leap** | Browser wallet (multi-chain Cosmos) | https://docs.leapwallet.io | -| **CosmosKit** | Multi-wallet abstraction for React | https://github.com/cosmology-tech/cosmos-kit | +| **CosmosKit** | Multi-wallet abstraction for React | https://cosmoskit.com | ### Cosmos-Specific Requirement Cosmos yields require the user's **public key** as an argument when calling the action From e0967865a3c72d00a12557446b1d73353d99b172 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:57:23 +0200 Subject: [PATCH 14/23] Reapply "docs(builder-skill): fix dead third-party wallet/lib doc links" This reverts commit 8456bf50b2760f2931b81f03403c65482350520a. --- .../references/signing-patterns.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index 244b787..95d3311 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -127,10 +127,10 @@ For apps where the user signs in the browser with an injected wallet. | Wallet | SDK / Integration | Docs | |---|---|---| | **MetaMask** | MetaMask SDK (`@metamask/sdk`) | https://docs.metamask.io/wallet/connect/ | -| **Phantom (EVM)** | Phantom provider (`window.phantom.ethereum`) | https://docs.phantom.app/solana/integrating/extension | -| **WalletConnect** | WalletConnect Web3Modal (`@web3modal/ethers` or `@web3modal/wagmi`) | https://docs.walletconnect.com/web3modal/about | +| **Phantom (EVM)** | Phantom provider (`window.phantom.ethereum`) | https://docs.phantom.com | +| **WalletConnect** | WalletConnect Web3Modal (`@web3modal/ethers` or `@web3modal/wagmi`) | https://docs.reown.com/appkit/overview | | **Rainbow** | RainbowKit (`@rainbow-me/rainbowkit`) | https://www.rainbowkit.com/docs/introduction | -| **Coinbase Wallet** | Coinbase Wallet SDK (`@coinbase/wallet-sdk`) | https://docs.cloud.coinbase.com/wallet-sdk/docs | +| **Coinbase Wallet** | Coinbase Wallet SDK (`@coinbase/wallet-sdk`) | https://github.com/coinbase/coinbase-wallet-sdk | ### Multi-Wallet Abstraction @@ -139,7 +139,7 @@ For supporting multiple wallets with a single integration: | Library | What It Does | Docs | |---|---|---| | **wagmi + viem** | React hooks for wallet connection, signing, and state | https://wagmi.sh | -| **Web3Modal** | Drop-in modal supporting 300+ wallets via WalletConnect | https://docs.walletconnect.com/web3modal/about | +| **Web3Modal** | Drop-in modal supporting 300+ wallets via WalletConnect | https://docs.reown.com/appkit/overview | | **RainbowKit** | Polished wallet connection UI for React apps | https://www.rainbowkit.com/docs/introduction | | **ConnectKit** | Wallet connection modal by Family | https://docs.family.co/connectkit | @@ -171,8 +171,8 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | Library | Use Case | Docs | |---|---|---| | **@solana/web3.js v2** | General-purpose Solana (new version) | https://www.npmjs.com/package/@solana/web3.js | -| **@solana/web3.js v1** | Legacy projects | https://solana-labs.github.io/solana-web3.js/ | -| **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.app/solana/integrating/extension | +| **@solana/web3.js v1** | Legacy projects | https://github.com/solana-labs/solana-web3.js | +| **Phantom** | Browser wallet (most popular Solana wallet) | https://docs.phantom.com | | **Backpack** | Browser wallet (multi-chain, popular in Solana) | https://docs.backpack.app | | **Solana Wallet Adapter** | Multi-wallet abstraction for React | https://github.com/anza-xyz/wallet-adapter | @@ -194,7 +194,7 @@ See `common-pitfalls.md` entries #4, #5, #11 for detailed explanations of each i | **@cosmjs/proto-signing** | Protobuf-based signing | https://cosmos.github.io/cosmjs/ | | **Keplr** | Browser wallet (most popular Cosmos wallet) | https://docs.keplr.app/api/ | | **Leap** | Browser wallet (multi-chain Cosmos) | https://docs.leapwallet.io | -| **CosmosKit** | Multi-wallet abstraction for React | https://cosmoskit.com | +| **CosmosKit** | Multi-wallet abstraction for React | https://github.com/cosmology-tech/cosmos-kit | ### Cosmos-Specific Requirement Cosmos yields require the user's **public key** as an argument when calling the action From d3560974efc64a0034967ec397f25ead7be608a4 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:09:22 +0200 Subject: [PATCH 15/23] docs(builder-skill): final canonical-truth sweep fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four adversarial QA agents (consistency, live-accuracy, coverage, end-to-end dogfood) verified the skill is canonical truth on the big-ticket items and found a focused set of real bugs — fixed here: - status.enter/status.exit are NOT query filters (live → 400); read per-item or use sort=statusEnterDesc — removed from the discovery filter list - provider/providers filter matches `providerId` (aave/compound/morpho), not the version slug (aave-v3 → total 0); fixed example values + response-shape providerId - add the `prime` query param to the /v1/yields table - mechanics.arguments.exit (and .enter) can be null — gate exit on status.exit AND arguments.exit != null; some yields exit only via a pendingAction - outputTokenBalance is optional on balance items; REDELEGATE isn't a real PendingActionDto.type → use DELEGATE/RESTAKE; CLP exit tokenId comes from BalanceDto.tokenId - missing-chain-arg error is 400 (not 422) — fixed cosmos/tezos/tron guides - /risk returns { updatedAt, stakingRewards?: { rating, score, ... } } (letter grade, often absent) — replaced the invented risk.level Low/Medium/High shape in policies.md - README: 16 pitfalls (not 12), full folder tree incl chains/, Tron in signing coverage - signing-patterns: 80+ networks (was 90+); ton.md signing stub de-stubbed Note: liquid_staking kept / fixed_yield kept absent per product decision (not bugs). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/README.md | 12 +++-- .../references/chains/cosmos.md | 2 +- .../references/chains/evm.md | 5 ++ .../references/chains/tezos.md | 2 +- .../references/chains/ton.md | 27 ++++++++-- .../references/chains/tron.md | 2 +- .../references/policies.md | 51 ++++++++++++++----- .../references/signing-patterns.md | 2 +- .../references/transaction-lifecycle.md | 30 ++++++++--- .../references/yield-types.md | 12 +++-- 10 files changed, 111 insertions(+), 34 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md index 49b3d7a..60cbfbd 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md @@ -97,12 +97,18 @@ yield-agentkit-builder/ ├── README.md # This file └── references/ ├── setup.md # Prerequisites and API key setup + ├── mcp-tools.md # Yield.xyz AgentKit MCP tools reference ├── common-pitfalls.md # Known errors and how to avoid them ├── api-field-mapping.md # How to look up endpoints and schemas from docs.json + ├── api-limits.md # Rate limits, pagination, and error codes ├── signing-patterns.md # Wallet SDKs and signing guidance per chain + ├── transaction-lifecycle.md # Action -> sign -> broadcast -> submit-hash flow ├── integration-patterns.md # Architecture per product type (custody, wallet, neobank, etc.) + ├── yield-types.md # Yield types and their argument shapes ├── output-formats.md # Display rules for generated UI code - └── policies.md # API rate limits, caching, best practices + ├── dashboard-and-api-keys.md # Dashboard usage and API key management + ├── policies.md # API rate limits, caching, best practices + └── chains/ # Per-chain signing guides (EVM, Cosmos, Solana, Tron, TON, …) ``` --- @@ -111,8 +117,8 @@ yield-agentkit-builder/ | Reference | What's in it | |---|---| -| `common-pitfalls.md` | 12 real errors/common pitfalls — wrong URLs, field names, gas issues, etc. | -| `signing-patterns.md` | Recommended SDKs for MetaMask, Phantom, WalletConnect, Rainbow, Coinbase, Solana, Cosmos | +| `common-pitfalls.md` | 16 real errors/common pitfalls — wrong URLs, field names, gas issues, etc. | +| `signing-patterns.md` | Recommended SDKs for MetaMask, Phantom, WalletConnect, Rainbow, Coinbase, Solana, Cosmos, Tron | | `integration-patterns.md` | Architecture for custody, wallet, neobank, aggregator, enterprise, mobile | --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md index 4dda360..0c1d12d 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cosmos.md @@ -117,7 +117,7 @@ for (const tx of action.transactions) { ## Common Gotchas -1. **cosmosPubKey missing → 422 error**: The most common Cosmos error. Always include the flat `cosmosPubKey` field (bech32 `cosmospub1...`) — there is no `additionalAddresses` wrapper. +1. **cosmosPubKey missing → 400 error**: The most common Cosmos error. A missing or misplaced `cosmosPubKey` returns `400 Bad Request` with a `validation.message[]`. Always include the flat `cosmosPubKey` field (bech32 `cosmospub1...`) — there is no `additionalAddresses` wrapper. 2. **21-day unbonding period**: Native ATOM staking has a 21-day unbonding. After `actions_exit`, the balance shows `type: "exiting"` for 21 days before becoming `type: "withdrawable"`. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md index de992ac..2fa616d 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/evm.md @@ -46,6 +46,11 @@ Arguments vary by yield **type** — do not assume every yield takes only `amoun | `liquidity_pool` | `amounts` — an **ARRAY** (one entry per pool token, e.g. `["1.0", "1000"]`), not `amount` | — | | `concentrated_liquidity_pool` | `amount` + optional `rangeMin`/`rangeMax` (decimal-string price bounds — omit both for a full-range position) + optional `inputToken`/`inputTokenNetwork` | `percentage` (0–100) **and** `tokenId` (position NFT id) — **both required, NO `amount`** | +For a CLP **exit**, `tokenId` is **not** something you compute — it's the position NFT id +from the user's balances. Fetch `GET /v1/yields/{id}/balances` (or `POST /v1/yields/balances`) +and read `BalanceDto.tokenId` off the position you want to exit. The same balance entry also +carries `priceRange` (the position's price bounds) for display. + Type-specific notes: - **vault enter:** optional `receiverAddress`, `useMaxAllowance` (bool). **vault exit:** optional `useMaxAmount` (bool); `amount` optional. - **restaking enter:** may expose `inputToken` (with an options list) and `minimum`. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md index 4f280ef..1732437 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tezos.md @@ -39,7 +39,7 @@ for (const tx of action.transactions) { ## Common Gotchas -1. **tezosPubKey required**: Always include the flat `tezosPubKey` field (inside `arguments`, not under any `additionalAddresses` wrapper) or you'll get a 422 error. +1. **tezosPubKey required**: Always include the flat `tezosPubKey` field (inside `arguments`, not under any `additionalAddresses` wrapper) or you'll get a `400 Bad Request` with a `validation.message[]`. 2. **Delegation vs staking**: Tezos uses "delegation" (baking) rather than direct staking. Delegated funds remain liquid. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md index 3c1e944..3c43fba 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/ton.md @@ -17,8 +17,14 @@ For `ton-ton-tston-staking`, `requiresValidatorSelection=false` and enter requir ## Signing +> **This is an outline, not a drop-in snippet.** TON's BOC handling and message +> construction are non-trivial, and the exact deserialization depends on the payload the +> API returns. Use the `@ton/ton` library ([docs](https://docs.ton.org/develop/dapps/ts-sdk/overview), +> [repo](https://github.com/ton-org/ton)) and follow the reference signer in the +> Yield.xyz signers repo: https://github.com/stakekit/signers + ```typescript -import { TonClient, WalletContractV4 } from "@ton/ton"; +import { TonClient, WalletContractV4, internal, external, Cell, beginCell } from "@ton/ton"; import { mnemonicToPrivateKey } from "@ton/crypto"; const client = new TonClient({ endpoint: "https://toncenter.com/api/v2/jsonRPC" }); @@ -26,20 +32,33 @@ const keyPair = await mnemonicToPrivateKey(mnemonic.split(" ")); const wallet = WalletContractV4.create({ publicKey: keyPair.publicKey, workchain: 0 }); for (const tx of action.transactions) { + // unsignedTransaction is a JSON string carrying the message BOC (base64 cell data). const txData = JSON.parse(tx.unsignedTransaction); - // Create and sign the transfer + // Deserialize the message cell(s) from the BOC the API returned, e.g. + // const messageCell = Cell.fromBase64(txData.boc); + // then build the internal/external message(s) to send. The precise field names and + // how to turn the BOC into a `SendMode`/message list depend on the payload — consult + // the @ton/ton docs and the stakekit/signers reference implementation linked above. + const contract = client.open(wallet); const seqno = await contract.getSeqno(); + // sendTransfer signs and broadcasts. `messages` must be constructed from the BOC above; + // see the signers repo for the exact construction for Yield.xyz TON payloads. await contract.sendTransfer({ seqno, secretKey: keyPair.secretKey, - messages: [/* construct from txData */], + messages: [/* internal(...) message(s) built from the parsed BOC */], }); + // Obtain the tx hash from the result of the send. TON does not return a hash from + // sendTransfer directly — derive it from the external message you sent, or poll the + // wallet's transactions (client.getTransactions) for the just-broadcast message and + // read its hash. See the signers repo for the canonical approach. + const txHash = ""; + // Submit hash — MANDATORY - // TON uses a different hash format — get it from the transaction result await sdk.api.submitTransactionHash(tx.id, { hash: txHash }); } ``` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md index 16db35b..6a19a6c 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/tron.md @@ -67,7 +67,7 @@ for (const tx of action.transactions) { ## Common Gotchas -1. **tronResource required**: Always include `tronResource: "BANDWIDTH"` or `"ENERGY"`. Missing this causes a 422 error. +1. **tronResource required**: Always include `tronResource: "BANDWIDTH"` or `"ENERGY"`. Missing this causes a `400 Bad Request` with a `validation.message[]`. 2. **Resource model**: Tron uses BANDWIDTH and ENERGY resources instead of gas. Staking TRX gives you these resources. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md index 4702863..f39d6d4 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md @@ -48,17 +48,32 @@ Guidelines for efficient API usage in Yield.xyz integrations. ## Safety rules & pre-execution checks -### Risk Levels +### Risk Profiles -Every yield opportunity in the Yield.xyz API has an associated risk profile. Note there is -**no top-level `risk` field on the yield object** — fetch the risk profile from -`GET /v1/yields/{id}/risk`. Before executing any action, evaluate the risk: +There is **no top-level `risk` field on the yield object** — fetch the risk profile from +`GET /v1/yields/{id}/risk`. The response shape is: -| Risk Level | Description | Example | -|------------|-------------|---------| -| Low | Blue-chip protocols, audited, large TVL | Lido ETH staking, Aave USDC lending | -| Medium | Established protocols, moderate TVL | Smaller liquid staking providers | -| High | Newer protocols, smaller TVL, complex strategies | New vault strategies, leveraged positions | +```json +{ + "updatedAt": "2026-06-17T00:00:00.000Z", + "stakingRewards": { + "rating": "A-", + "score": 87, + "potentialRating": "A", + "potentialScore": 90, + "type": "...", + "riskMetrics": { } + } +} +``` + +`stakingRewards` is a [StakingRewards](https://www.stakingrewards.com) assessment: a +**letter `rating`** (e.g. `"A-"`, `"B+"`) plus a numeric `score`. There is **no +`level` field and no low/medium/high scale.** + +`stakingRewards` is **often absent entirely** — many yields return just `{ "updatedAt": ... }`. +Always treat `stakingRewards` (and therefore `.rating` / `.score`) as possibly `undefined` +and decide your own policy for yields with no rating (e.g. block, or require manual review). ### 6 Pre-Execution Checks @@ -88,7 +103,8 @@ You can implement additional guardrails in your application: ```json { "maxSingleTransactionUsd": 10000, - "allowedRiskLevels": ["low", "medium"], + "minStakingRewardsScore": 70, + "allowYieldsWithoutRating": false, "requireShieldValidation": true, "allowedNetworks": ["ethereum", "base", "arbitrum"], "requireUserConfirmation": true, @@ -107,10 +123,19 @@ async function executeWithGuardrails(params: { }) { const yield_ = await sdk.api.getYield(params.yieldId); - // Risk is NOT a field on the yield object — fetch it from GET /v1/yields/{id}/risk + // Risk is NOT a field on the yield object — fetch it from GET /v1/yields/{id}/risk. + // The shape is { updatedAt, stakingRewards?: { rating, score, ... } }; there is no + // `level` field, and `stakingRewards` is often absent. Handle the missing case. const risk = await sdk.api.getYieldRisk(params.yieldId); - if (!params.guardrails.allowedRiskLevels.includes(risk.level)) { - throw new Error(`Risk level ${risk.level} not allowed`); + const score = risk.stakingRewards?.score; + if (score === undefined) { + if (!params.guardrails.allowYieldsWithoutRating) { + throw new Error(`Yield ${params.yieldId} has no risk rating`); + } + } else if (score < params.guardrails.minStakingRewardsScore) { + throw new Error( + `Risk score ${score} (${risk.stakingRewards?.rating}) below minimum ${params.guardrails.minStakingRewardsScore}`, + ); } if (!params.guardrails.allowedNetworks.includes(yield_.network)) { diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index 95d3311..607931d 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -10,7 +10,7 @@ do not hardcode signing logic from snippets. This covers the `unsignedTransaction` format returned by the Yield.xyz API for each **chain family**. -> **All 90+ networks collapse into one of these families for signing purposes.** +> **All 80+ networks collapse into one of these families for signing purposes.** > Every network has a `category` field in `GET /v1/networks` — use that to determine which family's signing guide applies. > For the full per-chain signing example, see `references/chains/.md`. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md index 99d0ee4..4e8e91a 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md @@ -46,7 +46,11 @@ Filter / sort results by: - `type` — yield type (e.g. `staking`, `lending`, `vault`) - `sort` — e.g. `rewardRateDesc` - `limit` / `offset` — pagination (max `limit` 100; see yield-types.md) -- `status.enter` — only yields accepting new deposits + +> There is **no `status.enter` / `status.exit` query filter** (sending `?status.enter=true` +> returns **400** "property status.enter should not exist"). To find yields accepting new +> deposits, read `status.enter` / `status.exit` on each returned item, or sort with +> `sort=statusEnterDesc` / `sort=statusExitDesc` (those sort values are real). This `GET /v1/yields` discovery query is the opening of the lifecycle — pick a yield `id` from the results, then read its schema (Step 2) before building any action. @@ -90,6 +94,11 @@ console.log(yield_.mechanics.entryLimits); // { minimum: "0", maximum: null, subsequentMinimum: null } — or null entirely ``` +> **`mechanics.arguments.exit` (and `.enter`) can be `null`.** Some yields (e.g. some +> restaking) have no exit schema at all — exit surfaces only as a per-balance `pendingAction` +> (Step 9). Gate exit on **both** `status.exit` AND `mechanics.arguments.exit != null` before +> reading `.exit.fields`; don't blindly iterate `mechanics.arguments.exit.fields`. + ## Step 3: Call Action Endpoint Request the unsigned transactions: @@ -472,8 +481,9 @@ After entering, the position shows up in balances. This is also where follow-up **Single yield** — `POST /v1/yields/{yieldId}/balances`, body `{ address }`. The batch response is `{ items, errors }`, where each `items[]` entry is -`{ yieldId, balances, rewardRate, outputTokenBalance }`. `pendingActions` is nested -**per-balance** inside `items[].balances[]` — not at the top level. +`{ yieldId, balances, rewardRate, outputTokenBalance? }`. `outputTokenBalance` is +**optional** — present only for share-token / ERC-4626 yields, so use optional chaining. +`pendingActions` is nested **per-balance** inside `items[].balances[]` — not at the top level. ```typescript // REST API — batch (max 25 queries; network + address required per query) @@ -492,7 +502,7 @@ const res = await fetch("https://api.yield.xyz/v1/yields/balances", { // yieldId: "base-usdc-aave-v3-lending", // balances: [{ /* ...BalanceDto fields... */, pendingActions: [...] }], // rewardRate: { /* ... */ }, -// outputTokenBalance: { /* ... */ }, +// outputTokenBalance: { /* ... */ }, // optional — share-token / ERC-4626 yields only // }, // ], // errors: [], @@ -535,9 +545,11 @@ a withdrawal, restake. Each is a `PendingActionDto`: ```typescript interface PendingActionDto { intent: string; // "manage" - type: string; // the action to run — e.g. CLAIM_UNSTAKED, WITHDRAW, RESTAKE_REWARDS, REDELEGATE + type: string; // the action to run — e.g. CLAIM_UNSTAKED, WITHDRAW, RESTAKE_REWARDS, DELEGATE. + // Real PendingActionDto.type members include STAKE, UNSTAKE, CLAIM_REWARDS, + // RESTAKE_REWARDS, WITHDRAW, CLAIM_UNSTAKED, RESTAKE, DELEGATE, REVOTE, REBOND, MIGRATE, … passthrough: string; // opaque server blob — pass back VERBATIM, never construct or edit it - arguments?: FieldsSchema | null; // present only when the action needs extra input (e.g. REDELEGATE → validatorAddress) + arguments?: FieldsSchema | null; // present only when the action needs extra input (e.g. DELEGATE → validatorAddress) amount?: string; } ``` @@ -561,7 +573,7 @@ for (const item of res.items) { passthrough: pending.passthrough, // opaque blob — pass VERBATIM }; // Only send `arguments` if the pending action defines a non-null fields schema - // (e.g. REDELEGATE needs a validatorAddress). Read pending.arguments.fields the + // (e.g. DELEGATE needs a validatorAddress). Read pending.arguments.fields the // same way as enter fields (Step 2) to know what to supply. if (pending.arguments != null) { body.arguments = { /* values for pending.arguments.fields */ }; @@ -588,3 +600,7 @@ manage action returns its own `transactions[]` → branch on its `executionPatte submit-hash, and poll transaction status to terminal. Exiting a position is the same flow via `POST /v1/actions/exit`. A complete runnable example is in the [api-recipes repo](https://github.com/stakekit/api-recipes). + +> **`concentrated_liquidity_pool` exit needs a `tokenId`.** For CLP exits, the required +> `tokenId` (and the `priceRange` for display) come from the position's `BalanceDto.tokenId` +> in the balances response (Step 8) — read it off the balance row, don't synthesize it. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md index 79bca66..008f84c 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/yield-types.md @@ -25,8 +25,9 @@ This is the single most important endpoint in the Yield.xyz API. It's the discov | `token` | string | `USDC` or token address | Filter by deposit/input token (symbol or address) | | `inputToken` | string | `USDC` | Alias/alternative to `token` | | `inputTokens` | comma-separated | `USDC,USDT,DAI` | **Multi-token query in one call** | -| `provider` | string | `aave-v3` | Filter by a single protocol | -| `providers` | comma-separated | `aave-v3,morpho,compound-v3` | **Multi-provider query in one call** | +| `provider` | string | `aave` | Filter by a single protocol (matches `providerId`, see note below) | +| `providers` | comma-separated | `aave,morpho,compound` | **Multi-provider query in one call** | +| `prime` | boolean | `true` / `false` | Filter to prime / curated yields | | `hasCooldownPeriod` | boolean | `true` / `false` | Only yields with (or without) an unbonding cooldown | | `hasWarmupPeriod` | boolean | `true` / `false` | Only yields with (or without) a warmup period | | `search` | string | `aave` | **Server-side name search** — don't `.filter()` on the client | @@ -34,6 +35,11 @@ This is the single most important endpoint in the Yield.xyz API. It's the discov | `limit` | integer | `20` | Page size | | `offset` | integer | `0` | Paging cursor | +> **`provider` matches `providerId`, not the yield-id version slug.** The filter matches the +> yield's `providerId` field (`aave`, `compound`, `morpho`) — **not** the version slug in the +> yield id (`aave-v3`, `compound-v3`). `?provider=aave-v3` returns total 0; `?provider=aave` +> returns results. + ### Sort options (`YieldSortingOption` enum) | Value | Meaning | @@ -65,7 +71,7 @@ This is the single most important endpoint in the Yield.xyz API. It's the discov "entryLimits": { ... }, "arguments": { "enter": { "fields": [ ... ] }, "exit": { "fields": [ ... ] } } }, - "providerId": "aave-v3", + "providerId": "aave", "metadata": { "name": "...", "logoURI": "...", "deprecated": false } } ] From 70cf69cbf7ebc47c9282f1427f98418f0d8539ae Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:15:19 +0200 Subject: [PATCH 16/23] refactor(builder-skill): tighten SKILL.md; fix Aptos framing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SKILL.md: merge the two "inspect the live API" workflow steps into one and trim Step "Generate Code" to point at the Quickstart for the lifecycle instead of re-listing it (the Quickstart already has the canonical flow). Steps renumber to 0–4; saves runtime tokens on the always-loaded file. - Aptos guide: it's a per-key enablement state, not "unsupported" — Aptos yields exist and can be enabled on a key (see dashboard-and-api-keys.md). Reframed the "no live yields / not integrable" callout accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/SKILL.md | 57 +++++++------------ .../references/chains/aptos.md | 4 +- 2 files changed, 21 insertions(+), 40 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 0f4da5c..11fd825 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -319,50 +319,31 @@ implementing one — use **`yield_list_repos`** to find the relevant public repo (widget, SDK, api-recipes, signers, shield, etc.) and read its raw source as a working reference. -### Step 2 — Look Up the API Spec +### Step 2 — Inspect the Live Spec & Real Responses -Before generating any code, fetch the live OpenAPI spec to discover current field -names and request/response shapes: +Ground every field name in the live API before generating code — never assume from memory: -**Option A — Use the doc tool:** -``` -yield_get_api_spec({ endpoint: "/v1/actions/enter", section: "endpoints" }) -``` - -**Option B — Fetch directly with the user's key:** -```bash -curl https://api.yield.xyz/docs.json | jq '.paths["/v1/actions/enter"]' -``` - -Use the spec as the source of truth. Never assume field names from memory. - -### Step 3 — Call the Real API to See Actual Responses - -Use the user's API key to make a real API call and inspect the response. This lets you -see the actual field names, nesting, and data types in the response: - -```bash -curl -s "https://api.yield.xyz/v1/yields?network=base&token=USDC&limit=1" \ - -H "x-api-key: $YIELD_API_KEY" | jq . -``` - -Use this real response as the reference when building the frontend or backend integration. +- **Spec:** `yield_get_api_spec({ endpoint: "/v1/actions/enter" })`, or + `curl https://api.yield.xyz/docs.json | jq '.paths["/v1/actions/enter"]'`. +- **Real response:** call the endpoint with the user's key and inspect the actual shape: + ```bash + curl -s "https://api.yield.xyz/v1/yields?network=base&token=USDC&limit=1" \ + -H "x-api-key: $YIELD_API_KEY" | jq . + ``` -### Step 4 — Generate Code +Build against what the spec and live response show — not the shapes in these docs. -Generate code that: -1. Uses the base URL `api.yield.xyz` -2. Passes the user's API key via `x-api-key` header -3. Uses field names exactly as seen in the live spec and API responses -4. Handles the full transaction lifecycle. **Always report the hash after - broadcasting** via `PUT /v1/transactions/{txId}/submit-hash` (action -> sign -> - broadcast -> submit-hash). -5. Follows the signing pattern appropriate for the user's product type and wallet choice +### Step 3 — Generate Code -See **[`references/signing-patterns.md`](./references/signing-patterns.md)** for -wallet SDK references and signing guidance per chain. +Generate code that uses `api.yield.xyz` with the key in the `x-api-key` header, uses +field names exactly as the live spec/responses show, follows the full transaction +lifecycle from the **Quickstart** above (and +[`references/transaction-lifecycle.md`](./references/transaction-lifecycle.md) for status +states, errors/retries, and exit/manage), and applies the signing pattern for the user's +product type and wallet — see +[`references/signing-patterns.md`](./references/signing-patterns.md). -### Step 5 — Run the Project Yourself and Report URLs +### Step 4 — Run the Project Yourself and Report URLs **Do not tell the user "now run `npm run dev`" and walk away.** The skill's job isn't done until the project is actually running and you've handed the user the diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md index 1940f3a..d065f98 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md @@ -1,6 +1,6 @@ # Aptos Integration Guide -> **No live yields currently.** Aptos has 0 live yields — `aptos-apt-native-staking` returns HTTP 400 and is not integrable today. The signing reference below is kept for when a yield goes live, but there is nothing to integrate yet. +> **Aptos yields may not be enabled on your API key.** `GET /v1/yields?network=aptos` can return 0 results and a direct yield id may return `400 "not enabled for this project"` — that's a per-key enablement state (see `dashboard-and-api-keys.md`), not "Aptos is unsupported." Aptos yields exist and can be enabled on a key; confirm availability for the user's key (or enable it in the dashboard) before building. ## unsignedTransaction Format @@ -49,4 +49,4 @@ curl "https://api.yield.xyz/v1/yields?network=aptos" \ -H "x-api-key: YOUR_KEY" ``` -No live yieldIds currently. `aptos-apt-native-staking` returns HTTP 400. +If `network=aptos` returns no results or a yield id returns `400 "not enabled for this project"`, the yield isn't enabled on your key — enable it in the dashboard or confirm availability, rather than assuming Aptos is unsupported. From 3cc2bb84891433b914c716b09ebe707eae24ed07 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:17:16 +0200 Subject: [PATCH 17/23] =?UTF-8?q?docs(builder-skill):=20drop=20"no=20live?= =?UTF-8?q?=20yields"=20framing=20=E2=80=94=20availability=20is=20per-key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yields are enabled/disabled per API key, so no chain guide should claim a network has no yields or "isn't integrable." Removed the Aptos "may not be enabled / 0 results" callout and trailing caveat; Aptos now reads like every other chain guide (Available Yields → `aptos-apt-native-staking`). Swept the chain guides — no no-yields/not- integrable framing remains. (The `400 "not enabled for this project"` error itself is still documented where it belongs — dashboard-and-api-keys.md / common-pitfalls — as an error a builder may hit, not as a claim that a network lacks yields.) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/references/chains/aptos.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md index d065f98..cfcd4de 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md @@ -1,7 +1,5 @@ # Aptos Integration Guide -> **Aptos yields may not be enabled on your API key.** `GET /v1/yields?network=aptos` can return 0 results and a direct yield id may return `400 "not enabled for this project"` — that's a per-key enablement state (see `dashboard-and-api-keys.md`), not "Aptos is unsupported." Aptos yields exist and can be enabled on a key; confirm availability for the user's key (or enable it in the dashboard) before building. - ## unsignedTransaction Format **Encoding:** JSON object with transaction payload @@ -49,4 +47,5 @@ curl "https://api.yield.xyz/v1/yields?network=aptos" \ -H "x-api-key: YOUR_KEY" ``` -If `network=aptos` returns no results or a yield id returns `400 "not enabled for this project"`, the yield isn't enabled on your key — enable it in the dashboard or confirm availability, rather than assuming Aptos is unsupported. +Common yieldIds: +- `aptos-apt-native-staking` From 018670ad387776606f716be11b9974015f1332fa Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:26:04 +0200 Subject: [PATCH 18/23] docs(builder-skill): remove migration tombstones from mcp-tools.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill is settled — drop the "the MCP used to expose ~10 tools / those have been removed" notes and the "(Former MCP tool)" mapping column. They were transition scaffolding; the doc now presents the 5 doc tools + reference files as the design, no removal markers. The forward-useful Need→Read static-guidance table stays. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../references/mcp-tools.md | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md index 8fa7318..fc7a0f9 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md @@ -16,8 +16,6 @@ Consult this file whenever you're unsure if a given MCP tool is safe to invoke d The **one purpose** of action tools is to let an end-user execute yield flows via an AI agent — not to let a builder inspect schemas. For building, always go through the live REST API. -> **Note:** The MCP used to also expose ~10 static doc tools (`yield_get_overview`, `yield_get_chain_guide`, `yield_get_transaction_guide`, `yield_get_yields_endpoint_guide`, `yield_get_safety_rules`, `yield_get_api_limits`, `yield_get_tool_reference`, `yield_list_doc_topics`, `yield_recommend_stack`, `yield_get_integration_guide`). Those have been removed — their content now lives in this skill's `references/` files. If you see them referenced anywhere, read the corresponding skill file instead (mapping below). - --- ## ✅ Doc Tools — Use These @@ -39,8 +37,6 @@ Fetches the **live** Yield OpenAPI spec (`https://api.yield.xyz/docs.json`, defa **Use when:** Before generating any integration code. Always verify current field names against this tool — don't trust memory. -> **Chain formats, transaction lifecycle, yield types, safety rules, and API limits used to be MCP doc tools (`yield_get_chain_guide`, `yield_get_transaction_guide`, `yield_get_yields_endpoint_guide`, `yield_get_safety_rules`, `yield_get_api_limits`).** They are no longer on the MCP — that guidance now lives in this skill's `references/` files. See [Static Guidance](#static-guidance--read-from-this-skills-reference-files). - ### `yield_troubleshoot_error` - Diagnoses API errors, HTTP status codes, or unexpected responses. For unrecognized errors, automatically looks up the live OpenAPI spec for the authoritative error shape. @@ -163,17 +159,15 @@ User wants to actually run/test an These topics are **not** MCP tools. Read the file directly when you need the guidance. -| Need | Read | (Former MCP tool) | -|---|---|---| -| `/v1/yields` query params + the 8 yield types & mechanics | `references/yield-types.md` | `yield_get_yields_endpoint_guide` | -| End-to-end transaction flow (discover → sign → submit-hash → confirm) | `references/transaction-lifecycle.md` | `yield_get_transaction_guide` | -| Per-chain `unsignedTransaction` format & signing SDK | `references/signing-patterns.md` + `references/chains/.md` | `yield_get_chain_guide` | -| Safety rules, pre-execution checks, guardrails | `references/policies.md` | `yield_get_safety_rules` | -| Rate limits, key tiers, throttling | `references/api-limits.md` | `yield_get_api_limits` | -| Choosing an integration approach (widget / SDK / REST / programmatic) | `references/setup.md` | `yield_recommend_stack` | -| Integration architecture patterns by product type | `references/integration-patterns.md` | `yield_get_integration_guide` | - -The action-tool table above replaces the former `yield_get_tool_reference` and `yield_get_overview` tools. +| Need | Read | +|---|---| +| `/v1/yields` query params + the 8 yield types & mechanics | `references/yield-types.md` | +| End-to-end transaction flow (discover → sign → submit-hash → confirm) | `references/transaction-lifecycle.md` | +| Per-chain `unsignedTransaction` format & signing SDK | `references/signing-patterns.md` + `references/chains/.md` | +| Safety rules, pre-execution checks, guardrails | `references/policies.md` | +| Rate limits, key tiers, throttling | `references/api-limits.md` | +| Choosing an integration approach (widget / SDK / REST / programmatic) | `references/setup.md` | +| Integration architecture patterns by product type | `references/integration-patterns.md` | --- From 248cb6e384a76ec51198e7683c8c2e8469c66eac Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:28:37 +0200 Subject: [PATCH 19/23] docs(builder-skill): drop "legacy API" tombstone framing in pitfalls #1/#2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reframe the wrong-base-URL and wrong-field-name pitfalls to keep the protective guard (use yieldId/address/arguments, not integrationId/addresses/args — agents hallucinate the old names from training data) without narrating "the legacy API used X". Table header "Wrong (legacy)" → "Don't use / Use". Remaining `legacy`/`deprecated` mentions are real protocol/API terms (EVM type 0=legacy, Solana legacy-vs-v0 tx, older-lib descriptors, the metadata.deprecated field) — left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../references/common-pitfalls.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md index 07705b1..bf24b20 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md @@ -8,10 +8,8 @@ Real errors encountered during builder sessions. Read this before generating any **Error:** Using `api.stakek.it` or `api.stakekit.io` instead of `api.yield.xyz`. -**What happens:** The legacy API uses completely different field names (`integrationId` -instead of `yieldId`, `addresses` instead of `address`, `args` instead of `arguments`). -Code built against the legacy API will fail silently or return 400 errors on the -correct API. +**What happens:** Requests go to the wrong host — they fail, or return field names and +shapes that don't match `api.yield.xyz`, so the integration breaks. **Fix:** Always use `https://api.yield.xyz`. No exceptions. If you see `stakek.it` or `stakekit.io` in any documentation, URL, or code — it's outdated. Replace it. @@ -20,9 +18,10 @@ correct API. ## 2. Wrong Field Names in Action Requests -**Error:** Using legacy field names in request bodies. +**Error:** Using the wrong field names in request bodies. These incorrect names are a +common mistake — they show up in stale examples and model memory. Use the correct ones: -| Wrong (legacy) | Correct (`api.yield.xyz`) | +| Don't use | Use | |---|---| | `integrationId` | `yieldId` | | `addresses` | `address` | From 5348c302dd4f22166464887c4875e848145e47fd Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:56:20 +0200 Subject: [PATCH 20/23] fix: fixes --- .../skills/yield-agentkit-builder/SKILL.md | 7 ++--- .../references/api-field-mapping.md | 5 +--- .../references/common-pitfalls.md | 30 ++++++++----------- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 11fd825..f288ec6 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -69,11 +69,8 @@ the `yield-agentkit` skill — not this one. ### 1. API Base URL The production base URL is `https://api.yield.xyz`, with the live OpenAPI spec at -`https://api.yield.xyz/docs.json`. - -This is the only correct production URL. Do not use `api.stakek.it`, -`api.stakekit.io`, or any other legacy domain. Every code sample, fetch call, -and SDK config must use this base URL. +`https://api.yield.xyz/docs.json`. Every code sample, fetch call, and SDK config must +use this base URL. ### 2. API Key Requirement diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md index 97be1f3..0230ed8 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md @@ -1,9 +1,6 @@ # API Reference -**API Base URL:** `https://api.yield.xyz` - -This is the only correct production URL. Do not use `api.stakek.it`, `api.stakekit.io`, -or any other legacy domain. +**API Base URL:** `https://api.yield.xyz` (live OpenAPI spec at `https://api.yield.xyz/docs.json`). --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md index bf24b20..b8c9cfc 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/common-pitfalls.md @@ -6,32 +6,26 @@ Real errors encountered during builder sessions. Read this before generating any ## 1. Wrong API Base URL -**Error:** Using `api.stakek.it` or `api.stakekit.io` instead of `api.yield.xyz`. +**Error:** Pointing requests at a host other than `https://api.yield.xyz`. -**What happens:** Requests go to the wrong host — they fail, or return field names and -shapes that don't match `api.yield.xyz`, so the integration breaks. +**What happens:** Requests fail or hit the wrong service. -**Fix:** Always use `https://api.yield.xyz`. No exceptions. If you see `stakek.it` or -`stakekit.io` in any documentation, URL, or code — it's outdated. Replace it. +**Fix:** Every fetch call and SDK config must use `https://api.yield.xyz` (live OpenAPI +spec at `https://api.yield.xyz/docs.json`). Don't hardcode or guess a host. --- -## 2. Wrong Field Names in Action Requests +## 2. Guessing Field Names Instead of Reading the Spec -**Error:** Using the wrong field names in request bodies. These incorrect names are a -common mistake — they show up in stale examples and model memory. Use the correct ones: +**Error:** Assuming request/response field names from memory instead of the live spec. -| Don't use | Use | -|---|---| -| `integrationId` | `yieldId` | -| `addresses` | `address` | -| `args` | `arguments` | -| `args.amount` | `arguments.amount` | +**What happens:** `400 Bad Request` with no helpful error message when a field name +doesn't match. -**What happens:** `400 Bad Request` with no helpful error message. - -**Fix:** Always fetch the live OpenAPI spec from `https://api.yield.xyz/docs.json` -before generating code. Never rely on field names from memory or cached documentation. +**Fix:** Action request bodies use exactly `yieldId`, `address`, and `arguments` (e.g. +`arguments.amount`). Always confirm field names against the live OpenAPI spec +(`https://api.yield.xyz/docs.json` or `yield_get_api_spec`) before generating code — +never rely on memory or cached docs. --- From 2acf3ee622e4b4e4ebb4178b6d4220dcdac5e48f Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:31:47 +0200 Subject: [PATCH 21/23] =?UTF-8?q?docs(builder-skill):=20robustness=20revie?= =?UTF-8?q?w=20=E2=80=94=20fix=20build-breaking=20widget=20CSS=20path,=20v?= =?UTF-8?q?alidators=20shape,=20chain=20snippets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skills/yield-agentkit-builder/README.md | 27 +- .../skills/yield-agentkit-builder/SKILL.md | 266 +++++++----------- .../references/api-field-mapping.md | 2 +- .../references/api-limits.md | 35 ++- .../references/chains/aptos.md | 95 +++++-- .../references/chains/cardano.md | 42 ++- .../references/chains/near.md | 67 +++-- .../references/chains/stellar.md | 105 +++++-- .../references/chains/substrate.md | 54 ++-- .../references/chains/sui.md | 68 ++++- .../references/integration-patterns.md | 14 +- .../references/mcp-tools.md | 36 +-- .../references/output-formats.md | 5 + .../references/policies.md | 48 +--- .../references/scaffold.md | 197 +++++++++++++ .../references/setup.md | 133 +++++---- .../references/signing-patterns.md | 31 +- .../references/transaction-lifecycle.md | 4 +- 18 files changed, 794 insertions(+), 435 deletions(-) create mode 100644 yield-agentkit-skills/skills/yield-agentkit-builder/references/scaffold.md diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md index 60cbfbd..25ff5b9 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/README.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/README.md @@ -5,7 +5,7 @@ > **Build on Yield.xyz with AI agents.** This skill teaches any MCP-compatible AI agent how to generate production-ready code that integrates with the Yield.xyz APIs — staking, lending, vaults, and RWA across 80+ networks. -The skill works alongside the [Yield.xyz AgentKit MCP server](https://mcp.yield.xyz/mcp), which provides live access to the OpenAPI spec and to public reference repos for looking up schemas, field definitions, and working code during generation. +What you ship integrates with Yield.xyz through the [`@yieldxyz/sdk`](https://www.npmjs.com/package/@yieldxyz/sdk) or REST calls against `https://api.yield.xyz`. The [Yield.xyz AgentKit MCP server](https://mcp.yield.xyz/mcp) is an optional build-time reference: its doc tools give the agent live access to the OpenAPI spec and public reference repos for grounding field names and schemas while generating code. Nothing in the shipped integration calls the MCP — it is not a runtime dependency. --- @@ -36,14 +36,16 @@ names, proper transaction signing, and the full submit-hash lifecycle. | `yield-agentkit-rwakit-privy` | Enter RWA yields end-to-end via Privy | Signed & broadcast transactions | | **`yield-agentkit-builder`** | **Build apps that integrate Yield.xyz** | **Production-ready code** | -The explore/execute skills use MCP tools directly. The builder skill uses MCP tools for research but generates code that calls the REST API with the user's own API key. +The explore/execute skills use MCP tools directly at runtime. The builder skill is different: it generates code that calls the Yield.xyz SDK or REST API with the user's own API key, and only uses the MCP doc tools as a build-time reference while writing that code. --- ## Requirements -- An MCP-compatible AI agent (Claude Code, Codex, Gemini CLI, etc.) -- A Yield.xyz API key (get one at https://yield.xyz) +- A Yield.xyz API key (get one at https://yield.xyz) — this is what your shipped + integration uses against `https://api.yield.xyz` +- Optional: an MCP-compatible AI agent (Claude Code, Codex, Gemini CLI, etc.) if you + want the build-time doc tools while generating code --- @@ -63,9 +65,11 @@ Open your agent and say: Set up the yield-agentkit-builder skill ``` -The agent will read `references/setup.md` and automatically: -1. Check if the Yield.xyz AgentKit MCP is registered, and register it if not -2. Confirm the skill is loaded and ready +The agent will read `references/setup.md` and: +1. Confirm you have a Yield.xyz API key and can reach `https://api.yield.xyz` +2. Optionally register the Yield.xyz AgentKit MCP for build-time doc lookups (skippable — + the live spec is also available directly at `https://api.yield.xyz/docs.json`) +3. Confirm the skill is loaded and ready --- @@ -85,7 +89,7 @@ Build a React component that shows a user's yield portfolio How do I handle MetaMask transaction signing with Yield.xyz? ``` -The agent will look up the live API spec via MCP, then generate code using `https://api.yield.xyz` with correct field names. +The agent grounds field names against the live API spec (via the MCP doc tools if registered, otherwise by fetching `https://api.yield.xyz/docs.json` directly), then generates code that calls `https://api.yield.xyz` through the SDK or REST. --- @@ -100,14 +104,15 @@ yield-agentkit-builder/ ├── mcp-tools.md # Yield.xyz AgentKit MCP tools reference ├── common-pitfalls.md # Known errors and how to avoid them ├── api-field-mapping.md # How to look up endpoints and schemas from docs.json - ├── api-limits.md # Rate limits, pagination, and error codes + ├── api-limits.md # Rate limits, pagination, and caching ├── signing-patterns.md # Wallet SDKs and signing guidance per chain ├── transaction-lifecycle.md # Action -> sign -> broadcast -> submit-hash flow ├── integration-patterns.md # Architecture per product type (custody, wallet, neobank, etc.) ├── yield-types.md # Yield types and their argument shapes ├── output-formats.md # Display rules for generated UI code + ├── scaffold.md # Greenfield project skeletons ├── dashboard-and-api-keys.md # Dashboard usage and API key management - ├── policies.md # API rate limits, caching, best practices + ├── policies.md # Safety rules, pre-execution checks, guardrails └── chains/ # Per-chain signing guides (EVM, Cosmos, Solana, Tron, TON, …) ``` @@ -126,5 +131,5 @@ yield-agentkit-builder/ ## Related - [Yield.xyz AgentKit Skill](../yield-agentkit/README.md) — explore yields conversationally -- [Yield.xyz AgentKit MCP Server](https://mcp.yield.xyz/mcp) — the live tools +- [Yield.xyz AgentKit MCP Server](https://mcp.yield.xyz/mcp) — optional build-time doc tools - [Yield.xyz Docs](https://docs.yield.xyz/docs/getting-started) — official documentation diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index f288ec6..22fbe42 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -16,6 +16,13 @@ API usage across all supported chains and protocols. **Scope: this skill builds Yield integrations** — staking, lending, vaults, and RWA across 80+ networks. +**What ships vs. what's build-time:** the integration you ship always calls +`https://api.yield.xyz` directly — via the `@yieldxyz/sdk` or REST/curl. **Nothing the +user ships calls the MCP.** The MCP's doc tools are only a *build-time reference* for you +(the builder) — they let you ground field names against the live spec/docs while +generating code. The MCP is not a runtime dependency and not a hard prerequisite; if it's +unavailable, fetch `https://api.yield.xyz/docs.json` directly. + --- ## Quickstart — minimal end-to-end (Yield) @@ -40,6 +47,11 @@ code — the shapes below are a map, not a contract. 6. **Poll to confirm** — `GET /v1/transactions/{id}` until status is `CONFIRMED` (there are no webhooks — poll with backoff). +Non-TypeScript (Python, Go, …)? Every endpoint is plain REST — see +[`references/signing-patterns.md`](./references/signing-patterns.md) for server-side +signing (e.g. Python `eth-account`), and +[`references/chains/.md`](./references/chains/). + For the full lifecycle (status states, error/retry handling, exit/manage) see [`references/transaction-lifecycle.md`](./references/transaction-lifecycle.md); for per-chain signing and transaction decoding see @@ -86,45 +98,23 @@ Once you have the key: ### 3. Never Call MCP Action Tools in a Builder Session — and Never Advise the User to -The MCP server exposes **17 action tools** (e.g. `yields_get_all`, `yields_get`, -`yields_get_balances`, `yields_get_kyc_status`, `actions_enter`, `actions_exit`, -`actions_manage`, `submit_hash` — the full list with descriptions is in -[`references/mcp-tools.md`](./references/mcp-tools.md)). - -**In a builder session you must never call any of them, and never advise the user -to call them as part of their integration.** They return trimmed/reshaped responses -that omit fields present in the full API, so code built against them will be wrong — -and execution/signing belongs to the dedicated skills below, not to a code-generation -session. - -**For building, get data from the REST API instead.** Use the user's API key to call -the live REST endpoint directly (`https://api.yield.xyz/v1/...`) via `curl`, `fetch`, -or any HTTP client — that gives the full, unmodified -response to build against. The only MCP tools you use are the read-only **doc tools** -(`yield_get_api_spec`, `yield_list_repos`, etc.). - -**Two things you _may_ do with action tools:** - -1. **Explain what an action tool does, if the user asks.** Describing a tool is fine — - the one-line descriptions are in [`references/mcp-tools.md`](./references/mcp-tools.md). - Explaining ≠ calling. -2. **If the user actually wants to run or test action tools** (enter a position, check - live balances, manage rewards), that is out of scope for this skill. Point them to - the dedicated skill that fits — each carries the wallet, signing, and security - guidance that action-tool execution requires: - - | If the user wants to… | Use this skill | What it provides | - |---|---|---| - | Explore yields / see what tools & yields exist, no wallet needed | **`yield-agentkit`** | Conversational discovery + action-tool reference; no signing | - | Execute (enter/exit/manage) with an privy agentic wallet | **`yield-agentkit-privy`** | Privy wallet — policy, signing, broadcast (requires Privy) | - | Execute with a MoonPay wallet | **`yield-agentkit-moonpay`** | MoonPay wallet auth + signing (requires MoonPay + MCP) | - | Execute tokenized RWA yields (KYC/accreditation gated) | **`yield-agentkit-rwakit-privy`** | RWA access gating on top of Privy execution | - - Don't reproduce execution/signing steps here — they live in those skills by design. - -> 📖 **For the full MCP tool list — which are safe in builder sessions (doc tools) -> and which must never be called (action tools), plus a one-line description and REST -> equivalent for each — see [`references/mcp-tools.md`](./references/mcp-tools.md).** +The MCP server's **action tools** (`actions_enter`, `actions_exit`, `submit_hash`, +`yields_get_balances`, etc.) return trimmed/reshaped responses that omit fields present +in the full API, so code built against them will be wrong. **Never call them, and never +advise the user to call them as part of their integration.** Build against the REST API +instead — call the live endpoint directly with the user's key (`https://api.yield.xyz/v1/...`) +for the full, unmodified response. The only MCP tools you use are the read-only **doc +tools** (`yield_get_api_spec`, `yield_list_repos`, etc.). + +Explaining what an action tool does is fine (explaining ≠ calling). If the user actually +wants to *run* one — enter/exit/manage, check live balances — that's out of scope here; +redirect them to the matching execute skill (`yield-agentkit`, `yield-agentkit-privy`, +`yield-agentkit-moonpay`, `yield-agentkit-rwakit-privy`), which carry the wallet/signing +guidance execution requires. + +> 📖 **Full action-tool list + which are safe (doc tools) vs. never-call, with a one-line +> description and REST equivalent for each — see +> [`references/mcp-tools.md`](./references/mcp-tools.md).** ### 4. Never Hardcode Field Names — Always Fetch from the Live Spec @@ -150,128 +140,58 @@ Gas insufficient? Ask user to add funds, call again. ### 6. Never Kill a Port Without Asking -When running the generated project locally (dev server, preview, etc.), you may hit a -port conflict because the user already has something running on that port. **Do not -automatically kill the process on that port.** The user may have another project, -service, or tool intentionally bound to it — killing it without consent can disrupt -their unrelated work. - -**Correct order of actions:** +When a local process (dev server, preview, DB, mock) hits a port conflict, **never kill +the occupying process automatically** — the user may have it intentionally bound. -1. **Try the default port first** (e.g. `3000` for most Node apps, `5173` for Vite, - `8000` for Python). If it's free, use it. -2. **If the default port is in use, try the next common free port** — `3001`, `3002`, - `5001`, `5173`, `8080`, `8081`, etc. Most frameworks accept a `PORT` env var or - `--port` flag (e.g. `PORT=3001 npm run dev`, `vite --port 5174`). -3. **Only if no reasonable alternative port is available** (or the framework can't - easily be rebound), **ask the user first** before killing anything: - > "Port 3000 is in use by another process and I couldn't find a free alternative. - > Would you like me to stop the process on 3000, or do you want to free it yourself?" -4. **Kill the port only after the user explicitly agrees.** - -This rule applies to every port-binding step: dev servers, preview tools, local -databases, mock services, etc. +1. **Try the default port** (`3000` Node, `5173` Vite, `8000` Python). If free, use it. +2. **If taken, fall back to a nearby free port** via `PORT=` env var or `--port` flag. +3. **Only as a last resort, ask the user** before killing anything — and kill only after + they explicitly agree. ### 7. Only Install Verified, Widely-Used Dependencies -Every dependency you add to the user's project is a supply-chain risk. Malicious or -typosquatted npm packages are a real attack vector — a single unverified `postinstall` -script can exfiltrate the user's `YIELD_API_KEY`, RPC keys, or wallet seed. - -**Default to the smallest possible dependency set.** Before `npm install`-ing -anything, apply this checklist: - -1. **Prefer the standard library / built-in APIs.** Modern Node and browsers cover - `fetch`, `crypto`, `URL`, `AbortController`, etc. — don't pull in `axios`, - `node-fetch`, `uuid`, etc. unless there's a concrete reason. -2. **Prefer packages already implied by the stack.** If the user is on Next.js, - use what Next ships. If on Vite + React, use the canonical ecosystem picks - (`@tanstack/react-query`, `zod`, `viem`, `wagmi`, etc.). -3. **Before installing any package the user hasn't mentioned, verify it on npm:** - - Published by a known, trusted maintainer or org (e.g. `@yieldxyz`, `wevm`, - `TanStack`, `solana-labs`, `cosmos`, `ethers`, `coinbase`, `metamask`, etc.) - - **High weekly download count** (rule of thumb: ≥ 100k/week for general-purpose - libs; lower is acceptable only for narrow niche packages with a clear maintainer) - - Recently maintained (release within the last 6–12 months) - - Has a GitHub repo, README, and no open security advisories - - Name matches exactly — watch for typosquats (`axois`, `requst`, `lodahs`, etc.) -4. **Only widely-used, verified packages install silently.** If a package fails any - of the checks above — obscure, low downloads, unknown maintainer, recently - renamed, unfamiliar — **stop and ask the user before installing**: - - > "The cleanest way to do X is the `foo-bar` package, but it only has ~2k - > weekly downloads and I'm not familiar with the maintainer. Want me to install - > it, pick a more popular alternative (`baz-qux`, 500k weekly downloads), or - > implement X inline without a dependency?" - -5. **Never install a package just because the user vaguely asked for functionality - it provides.** If a lightweight inline implementation (~30 lines) replaces a - sketchy dependency, write the inline version. - -This rule applies to both frontend and backend — `package.json`, `requirements.txt`, -`go.mod`, `Cargo.toml`, and any other dependency manifest. +Every dependency is a supply-chain risk — a single typosquatted package's `postinstall` +can exfiltrate the user's `YIELD_API_KEY`, RPC keys, or wallet seed. Default to the +smallest dependency set: prefer built-ins (`fetch`, `crypto`, …) and packages already +implied by the stack (`viem`, `wagmi`, `@tanstack/react-query`, etc.). Before installing +anything the user didn't name, verify it on npm (trusted maintainer, high weekly +downloads, recent release, real repo, exact name — no typosquats). If a package fails +those checks, **stop and ask** before installing (offer a popular alternative or a short +inline implementation). Applies to every manifest — `package.json`, `requirements.txt`, +`go.mod`, `Cargo.toml`. ### 8. Always Include Pagination When Listing Yields -Yield.xyz exposes **2,900+ yields across 80+ networks**. Popular chains like -Ethereum, Base, and Arbitrum each have hundreds of entries. A UI that renders the -full list in one page is unusable — slow to load, painful to scroll, and expensive -to re-render. - -**Pagination is a default, not a feature request.** Do not wait for the user to -ask. Every generated app that lists yields, balances, validators, or transactions -must ship with pagination wired in from the start. - -**Baseline requirements for any list view:** - -1. **Page size** — default to 20–50 items per page; never render the full list at once. -2. **Use the API's built-in pagination params** on `GET /v1/yields` (`limit`, - `offset` — the API is offset-only, max `limit` is 100; check `yield_get_api_spec` - for the current shape). Do **not** fetch everything client-side and slice — that - defeats the point. -3. **Provide a way to change pages** — next/prev buttons, numbered pager, or an - infinite-scroll sentinel (whichever fits the UI). -4. **Preserve filters across pages** — network, token, type, provider filters - must remain applied when paging. -5. **Show total count** (e.g. "1-20 of 1,847 yields") when the API returns it, so - the user understands the scale. -6. **Same rule for validators and balances** — validator lists (`yields_get_validators`) - and position lists (`yields_get_balances`) can also be long; paginate them too. -7. **Prefer server-side sort, search, and filter.** Before writing any `.sort()`, - `.filter()`, or `.toLowerCase().includes(...)` on the client, check the live - OpenAPI spec (`yield_get_api_spec({ endpoint: "/v1/yields" })` or - `curl https://api.yield.xyz/docs.json | jq '.paths["/v1/yields"]'`) for - `sort`, `search`, and filter query params on the endpoint. The `/v1/yields` - endpoint in particular supports server-side sorting (by APY, TVL, etc.) and - text search via query params. Pass those params through — don't fetch-all - and sort locally. A client-side sort across 3,000 yields both defeats the - pagination rule above and produces wrong results across pages (page 2 - sorted by APY on the client means two different sort contexts). - If a param you need really doesn't exist, fall back to client-side — but - that's a last resort, not a default. - -The user can always adjust page size or swap to infinite scroll later. The -non-negotiable is that **the first version you deliver must not render an -unbounded list**, and any sort / search / filter must go through the API -whenever the API supports it. +Yield.xyz exposes **2,900+ yields across 80+ networks**, so pagination is a default, not +a feature request. Every list view you generate — yields, balances, validators, +transactions — must ship paginated from the start; **never render an unbounded list.** + +- Use the API's built-in params on `GET /v1/yields` (`offset`/`limit`, offset-only, + max `limit` 100; default ~20–50 per page). Don't fetch-all client-side and slice. +- Provide page navigation, preserve active filters across pages, and show the total + count when the API returns it. +- **Sort, search, and filter via API query params, not on the client** — a client-side + sort across thousands of yields produces wrong results across pages. Check the live + spec for the available params; fall back to client-side only if a needed param truly + doesn't exist. --- ## Workflow -### Step 0 — Register the Yield.xyz MCP Server (do this FIRST, automatically) +### Step 0 — Register the MCP Doc Tools if Available (optional, best-effort) -**The very first action in every builder session** — before asking for an API key, -before asking what the user is building — is to ensure the `yield-agentkit` MCP -server is registered with the user's agent. This is **not optional**. The skill's -live tools (`yield_get_api_spec`, `yield_lookup_docs`, `yield_fetch_doc`, -`yield_troubleshoot_error`, `yield_list_repos`) come from that MCP server, and without -them the skill will generate code from memory rather than from the live spec. (Static -guidance — chains, transaction lifecycle, yield types, safety — lives in this skill's -own `references/` files, not on the MCP.) +If you can, register the `yield-agentkit` MCP server so its read-only **doc tools** +(`yield_get_api_spec`, `yield_lookup_docs`, `yield_fetch_doc`, +`yield_troubleshoot_error`, `yield_list_repos`) are available to you while you build. +These help *you* (the builder) ground field names against the live spec and docs as you +generate code. This is a **build-time convenience, not a prerequisite** — the integration +you ship does not call the MCP, so don't block the build on it. If the MCP isn't +registered, fetch `https://api.yield.xyz/docs.json` directly instead. (Static guidance — +chains, transaction lifecycle, yield types, safety — lives in this skill's own +`references/` files, not on the MCP.) -**Do this automatically — do not ask the user to run the command themselves.** -Run the appropriate registration command for their agent, then verify it connected. +Registration command, for convenience — For Claude Code: ```bash @@ -282,8 +202,11 @@ claude mcp list # verify "yield-agentkit" shows "✓ Connected" For other agents (Codex, Gemini CLI, etc.), write the `yield-agentkit` entry into the appropriate MCP config file (`~/.mcp.json` or project-local `.mcp.json`). -If registration fails, stop and surface the error to the user — do not attempt to -build anything until the MCP is connected. Full details and config snippets are in +**If the MCP isn't available, just continue.** The one thing the doc tools give you is +easier live-spec lookup, and that's reachable without them: fetch +`https://api.yield.xyz/docs.json` directly (the `yield_list_repos` content is a +nice-to-have, not essential), so field-name grounding comes from `docs.json` + live +responses. Full details and config snippets are in [`references/setup.md`](./references/setup.md). ### Step 1 — Understand the Use Case & Recommend an Approach @@ -293,9 +216,26 @@ no product choice to make — go straight to understanding what the user is buil Ask what they're building; the answer determines the integration option, architecture, signing approach, and which reference files to load. -Recommend Widget for the fastest drop-in path, the SDK for TypeScript/JS apps that -want typed API access, or direct REST for everything else (other languages, custom -backends). Then map their product type to the signing/architecture pattern below. +**First, fork on existing app vs. new project — most clients have an existing app:** + +- **Existing app** (you're adding yield to a repo that already exists) → the deliverable + is a component/route or a few endpoints plus the install/wiring into *their* codebase. + **Do not scaffold a new project.** Match their stack, their conventions, their build. +- **New project** (greenfield, nothing exists yet) → scaffold a fresh project end-to-end. + +**Pick the integration option (default is decisive and situational):** + +- **Greenfield / unspecified staking app → `@stakekit/widget`** — fastest path to a + running, clickable app. + *Already have a wallet / wagmi setup? The widget doesn't force its own connect flow — + pass `externalProviders` (your address + signer) plus `disableInjectedProviderDiscovery`. + See [`references/integration-patterns.md`](./references/integration-patterns.md) → + "Bring your own signing infra".* +- **Existing React app needing custom UI → SDK or REST** — typed API access via + `@yieldxyz/sdk`, or call REST directly. +- **Non-JS (Python, Go, …) → REST** — every endpoint is plain HTTP. + +Then map their product type to the signing/architecture pattern below. Map the product type to architecture and signing: @@ -343,8 +283,15 @@ product type and wallet — see ### Step 4 — Run the Project Yourself and Report URLs **Do not tell the user "now run `npm run dev`" and walk away.** The skill's job -isn't done until the project is actually running and you've handed the user the -live URLs. Run every step yourself: +isn't done until the integration is actually running and you've shown the user it works. + +**If you integrated into an EXISTING app:** run *their* dev server (use their existing +`dev`/`start` script) and point the user at the specific route or endpoints you added — +verify those work. Don't try to scaffold or boot a new multi-service project; the +"Frontend / Backend / health URL" summary below is for greenfield new apps, not for a +route you dropped into a running app. + +**If you scaffolded a NEW project (greenfield):** run every step yourself: 1. **Install dependencies yourself** — `pnpm install` / `npm install` / `yarn install` based on the lockfile you generated. Surface install errors and fix them. @@ -397,19 +344,20 @@ before generating code for that topic.** | File | When to Read | |---|---| -| **[`references/mcp-tools.md`](./references/mcp-tools.md)** | **Before invoking any MCP tool** — which tools are safe (doc tools) vs. which must never be called (action tools) | +| **[`references/setup.md`](./references/setup.md)** | First-time setup — prerequisites, optional MCP doc-tool registration | +| **[`references/scaffold.md`](./references/scaffold.md)** | When starting a **new (greenfield) project** — recommended project layouts to scaffold | | **[`references/common-pitfalls.md`](./references/common-pitfalls.md)** | **Before generating any code** — known errors and how to avoid them | | **[`references/signing-patterns.md`](./references/signing-patterns.md)** | Before generating signing/wallet code — SDKs, libraries, chain-specific guidance | | **[`references/chains/.md`](./references/chains/)** | Before generating signing/decoding code for a specific chain (e.g. `evm.md`, `solana.md`, `cosmos.md`, `stellar.md`) — per-chain transaction signing and decoding | -| **[`references/api-field-mapping.md`](./references/api-field-mapping.md)** | When wiring requests/responses — endpoint reference and the error envelope shape | | **[`references/integration-patterns.md`](./references/integration-patterns.md)** | When user describes their product type — architecture per use case | | **[`references/output-formats.md`](./references/output-formats.md)** | When generating UI code — display rules, number formatting | -| **[`references/policies.md`](./references/policies.md)** | API usage limits, rate limiting, caching guidance | -| **[`references/setup.md`](./references/setup.md)** | First-time setup — prerequisites | +| **[`references/policies.md`](./references/policies.md)** | Safety rules & guardrails | +| **[`references/api-limits.md`](./references/api-limits.md)** | Rate limits, pagination, and caching guidance | | **[`references/dashboard-and-api-keys.md`](./references/dashboard-and-api-keys.md)** | How the dashboard/API key controls which yields & features are enabled — read when a yield "isn't available" or returns `400 not enabled` | | **[`references/yield-types.md`](./references/yield-types.md)** | `GET /v1/yields` query params + the yield-type categories (high level; DTO is source of truth) | -| **[`references/api-limits.md`](./references/api-limits.md)** | Rate limits, key tiers, throttling | | **[`references/transaction-lifecycle.md`](./references/transaction-lifecycle.md)** | End-to-end transaction flow: build → sign → broadcast → submit-hash → confirm. | +| **[`references/api-field-mapping.md`](./references/api-field-mapping.md)** | When wiring requests/responses — endpoint reference and the error envelope shape | +| **[`references/mcp-tools.md`](./references/mcp-tools.md)** | **Before invoking any MCP doc tool** — which tools are safe (doc tools) vs. which must never be called (action tools) | --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md index 0230ed8..4b68064 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-field-mapping.md @@ -138,7 +138,7 @@ x-api-key: YOUR_API_KEY The set of status codes the **application** returns is `400, 401, 403, 404, 412, 422, 429, 500`. A transient `502`/`503` can still reach you from the edge/infra layer (CDN, load balancer) -rather than the application — treat those as retryable with backoff (see `policies.md`). +rather than the application — treat those as retryable with backoff (see `api-limits.md`). | Code | Meaning | |---|---| diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md index 385b1c4..32a3404 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/api-limits.md @@ -44,9 +44,19 @@ async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) } ``` -### Cache Yield Metadata +### Cache By Data Type -Yield metadata (`GET /v1/yields/{id}`) changes infrequently. Cache it for 5-15 minutes to reduce API calls. +Cache data that changes infrequently to reduce API calls. Recommended durations: + +| Data Type | Cache Duration | Notes | +|---|---|---| +| Yield list (`/v1/yields`) | 5-15 minutes | APYs update periodically | +| Yield metadata (`/v1/yields/{id}`) | 5-15 minutes | Schema rarely changes | +| Validators | 15-30 minutes | Validator set changes slowly | +| Balances | Do not cache | Always fetch fresh | +| Networks | 1 hour | Rarely changes | + +Yield metadata (`GET /v1/yields/{id}`) is the highest-value thing to cache: ```typescript const yieldCache = new Map(); @@ -68,6 +78,13 @@ Use `GET /v1/yields` with filters instead of fetching individual yields: GET /v1/yields?network=ethereum&token=USDC ``` +### Fetch Only What You Need + +Use query parameters (`network`, `token`, `type`) to filter server-side rather than +fetching everything and filtering client-side. Don't poll balances excessively — check +after user actions, not on a tight loop. Set a **3-second timeout** on all API calls to +avoid hanging requests. + ### Poll for Status — There Are No Webhooks Yield.xyz has **no webhook, event, or callback endpoints**. To learn the status of an @@ -76,6 +93,20 @@ in-flight action or transaction, poll `GET /v1/transactions/{id}` (and intervals with backoff rather than a tight loop — see `transaction-lifecycle.md` for the recommended cadence and the full status flow. +### Other Efficiency Tips + +- **Use the SDK for TypeScript.** `@yieldxyz/sdk` handles pagination, typing, and error handling for you. +- **Log all submit-hash calls.** If a hash submission fails, positions won't update — you need to be able to retry. +- **Handle 503 gracefully.** Upstream protocols can be temporarily unavailable; treat as retryable with backoff. + +## Pagination + +- Default `limit`: 20 +- Maximum `limit`: **100** — a `limit` greater than 100 returns **HTTP 400** +- Use `offset` for pagination — pagination is **offset-only**, there is no cursor +- The response envelope is `{ items, total, offset, limit }` — the array key is **`items`** (not `data`) +- Do not attempt to fetch all yields in a single request + ## Getting a Production Key 1. Sign up at https://dashboard.yield.xyz diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md index cfcd4de..4f1938f 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md @@ -2,11 +2,20 @@ ## unsignedTransaction Format -**Encoding:** JSON object with transaction payload -**Parse before signing:** Yes — `JSON.parse(unsignedTransaction)` if string +**Encoding:** Serialized Aptos `RawTransaction` (BCS bytes, returned as a string) +**Parse before signing:** No — decode to bytes and deserialize into a `RawTransaction` with the SDK's `Deserializer`. Do NOT rebuild the payload. + +The transaction the API returns already encodes the Move entry-function call, the sender, +the sequence number, the gas config, and the expiration. Aptos is **Move-based**: the +payload references on-chain Move modules and functions, and the account **sequence number** +must match exactly — which is why you sign the bytes the API gives you rather than building +a fresh transaction. ## Required Arguments +When calling `POST /v1/actions/enter`, confirm the exact required fields via +`mechanics.arguments.enter.fields[]` (and `.exit.fields[]`) in the yield DTO — do not assume. + | Argument | Required | Description | |----------|----------|-------------| | `amount` | Yes | Human-readable string. `"10"` = 10 APT | @@ -14,31 +23,78 @@ ## Signing -```typescript -import { AptosClient, AptosAccount } from "aptos"; - -const client = new AptosClient("https://fullnode.mainnet.aptoslabs.com"); -const account = new AptosAccount(privateKeyBytes); +> **Sign the API's transaction VERBATIM.** The `RawTransaction` returned by Yield.xyz +> already embeds the sender, the account **sequence number**, the gas unit price / max gas, +> and the expiration timestamp. Do **NOT** re-fetch the account, regenerate the payload, or +> call `generateTransaction(...)` — deserialize the bytes the API returned, sign them as-is, +> and submit. Rebuilding produces a transaction over different bytes and the submit will fail +> (signature / sequence-number mismatch). -for (const tx of action.transactions) { - const payload = JSON.parse(tx.unsignedTransaction); +> **Note:** This uses the current SDK, `@aptos-labs/ts-sdk` — **not** the deprecated legacy +> `aptos` package (`AptosClient` / `AptosAccount` / `generateTransaction`). The legacy client +> rebuilds the transaction, which violates the sign-verbatim rule above. - // Sign and submit - const txnRequest = await client.generateTransaction(account.address(), payload); - const signedTxn = await client.signTransaction(account, txnRequest); - const result = await client.submitTransaction(signedTxn); - await client.waitForTransaction(result.hash); +```typescript +import { + Aptos, + AptosConfig, + Network, + Account, + Ed25519PrivateKey, + RawTransaction, + SimpleTransaction, + Deserializer, + Hex, +} from "@aptos-labs/ts-sdk"; + +const aptos = new Aptos(new AptosConfig({ network: Network.MAINNET })); +const signer = Account.fromPrivateKey({ privateKey: new Ed25519PrivateKey(PRIVATE_KEY) }); - // Submit hash — MANDATORY - await sdk.api.submitTransactionHash(tx.id, { hash: result.hash }); +for (const tx of action.transactions) { + // tx.unsignedTransaction is a serialized RawTransaction. Deserialize VERBATIM — + // do NOT rebuild the payload from chain state. + const rawTxnBytes = Hex.hexInputToUint8Array(tx.unsignedTransaction); + const transaction = new SimpleTransaction( + RawTransaction.deserialize(new Deserializer(rawTxnBytes)), + ); + + // Sign the deserialized transaction as-is → returns an AccountAuthenticator. + const senderAuthenticator = aptos.transaction.sign({ signer, transaction }); + + // Submit the signed transaction. + const pending = await aptos.transaction.submit.simple({ + transaction, + senderAuthenticator, + }); + await aptos.waitForTransaction({ transactionHash: pending.hash }); + + // Submit hash back to Yield.xyz — MANDATORY + await sdk.api.submitTransactionHash(tx.id, { hash: pending.hash }); + + // Wait for confirmation before the next stepIndex. } ``` +If the live payload turns out not to be a bare `RawTransaction` (the wire shape can change), +do not invent a decode path — follow the reference signer in the Yield.xyz signers repo +([github.com/stakekit/signers](https://github.com/stakekit/signers)) and the +[`@aptos-labs/ts-sdk` docs](https://aptos.dev/en/build/sdks/ts-sdk) for the exact +deserialization, the way the TON guide does. + ## Common Gotchas -1. **Move-based**: Aptos uses the Move language. Transaction payloads reference Move modules and functions. +1. **Move-based payloads**: Transaction payloads reference Move modules and entry functions. + The API encodes these for you — never reconstruct the entry-function call yourself. + +2. **Sequence number**: Each account has a sequence number that must match the chain. It is + baked into the `RawTransaction` the API returns — re-fetching the account or rebuilding the + tx will desync it and the submit fails. Sign the bytes as-is. -2. **Sequence number**: Each account has a sequence number that must match. The API handles this. +3. **Legacy `aptos` package**: The old `AptosClient` / `AptosAccount` / `generateTransaction` + API rebuilds the transaction. Use `@aptos-labs/ts-sdk` and sign the API's bytes verbatim. + +4. **Expiration**: The `RawTransaction` carries an expiration timestamp. If signing is delayed + past it, the submit fails — request a fresh action rather than patching the field. ## Available Yields @@ -49,3 +105,6 @@ curl "https://api.yield.xyz/v1/yields?network=aptos" \ Common yieldIds: - `aptos-apt-native-staking` + +If this query returns an empty list, the Aptos yields may not be enabled for your project — +contact Yield.xyz to enable them rather than assuming none exist. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md index e0b355f..f7e1182 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/cardano.md @@ -16,20 +16,29 @@ Confirm exact required fields via `mechanics.arguments.enter.fields[]` in the yi ## Signing +> **Sign the API's transaction VERBATIM — don't rebuild it.** The CBOR transaction the +> API returns already encodes the inputs (UTXO selection), outputs, certificates (stake +> registration + delegation), and fee. Deserialize it, hash the **body**, and attach a +> vkey witness over that hash — do **NOT** reconstruct the transaction body or re-select +> UTXOs. Rebuilding produces different body bytes and the witness will not verify. + ```typescript import * as CardanoWasm from "@emurgo/cardano-serialization-lib-nodejs"; +// Derive the payment signing key (PrivateKey) you sign with. Source it from your wallet — +// e.g. a Bip32 root key derived from the mnemonic, then the payment key at the standard +// CIP-1852 path (m/1852'/1815'/0'/0/0). Adapt to however your wallet stores keys. +const privateKey: CardanoWasm.PrivateKey = /* your payment signing key */; + for (const tx of action.transactions) { const txBytes = Buffer.from(tx.unsignedTransaction, "hex"); const transaction = CardanoWasm.Transaction.from_bytes(txBytes); - // Get the transaction body hash for signing + // Hash the body the API returned and witness it as-is — do NOT rebuild the body. const txHash = CardanoWasm.hash_transaction(transaction.body()); - // Sign const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new(); - const vkeyWitness = CardanoWasm.make_vkey_witness(txHash, privateKey); - vkeyWitnesses.add(vkeyWitness); + vkeyWitnesses.add(CardanoWasm.make_vkey_witness(txHash, privateKey)); const witnessSet = CardanoWasm.TransactionWitnessSet.new(); witnessSet.set_vkeys(vkeyWitnesses); @@ -37,25 +46,40 @@ for (const tx of action.transactions) { const signedTx = CardanoWasm.Transaction.new( transaction.body(), witnessSet, - transaction.auxiliary_data() + transaction.auxiliary_data(), ); - // Broadcast via Cardano node - const hash = await submitTransaction(signedTx.to_bytes()); + // Broadcast. PLACEHOLDER — pick one transport and return its tx hash: + // - Blockfrost: POST /tx/submit with the raw CBOR bytes (signedTx.to_bytes()), + // Content-Type application/cbor; the body of the response is the tx hash. + // - A cardano-submit-api / cardano-node you operate. + // The hash is the hex of CardanoWasm.hash_transaction(signedTx.body()). + const hash = await submitTransaction(signedTx.to_bytes()); // implement per your transport // Submit hash — MANDATORY await sdk.api.submitTransactionHash(tx.id, { hash }); } ``` +If the live payload turns out not to be a bare `Transaction` CBOR (the wire shape can change), +do not invent a decode path — follow the reference signer in the Yield.xyz signers repo +([github.com/stakekit/signers](https://github.com/stakekit/signers)) for the exact +deserialization, the way the TON guide does. + ## Common Gotchas -1. **UTXO model**: Cardano uses UTXOs, not accounts. The API handles UTXO selection. +1. **UTXO model**: Cardano uses UTXOs, not accounts. The API handles UTXO selection — it is + baked into the CBOR body the API returns. Do not re-select inputs or rebuild the body. -2. **Stake registration**: First-time stakers need a stake key registration transaction before delegation. The API includes this automatically. +2. **Stake registration**: First-time stakers need a stake key registration transaction before + delegation. The API includes this certificate automatically — just sign and submit. 3. **Pool saturation**: Delegating to saturated pools reduces rewards. Use `GET /v1/yields/{id}/validators` for pool metrics. +4. **Broadcast is your responsibility**: The Cardano serialization library signs but does not + broadcast. Wire up Blockfrost or a node submit endpoint (see the placeholder above) and + feed the returned tx hash to `submitTransactionHash`. + ## Available Yields ```bash diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md index 6f47316..81f77c3 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/near.md @@ -14,43 +14,48 @@ ## Signing -```typescript -import { connect, keyStores, KeyPair } from "near-api-js"; - -const keyStore = new keyStores.InMemoryKeyStore(); -await keyStore.setKey("mainnet", accountId, KeyPair.fromString(privateKey)); - -const near = await connect({ - networkId: "mainnet", - keyStore, - nodeUrl: "https://rpc.mainnet.near.org", -}); - -const account = await near.account(accountId); - -for (const tx of action.transactions) { - const txData = JSON.parse(tx.unsignedTransaction); - - // Execute the transaction - const result = await account.signAndSendTransaction({ - receiverId: txData.receiverId, - actions: txData.actions, - }); - - // Submit hash — MANDATORY - await sdk.api.submitTransactionHash(tx.id, { - hash: result.transaction.hash, - }); -} -``` +> **Sign the API's transaction VERBATIM — don't rebuild it.** The `unsignedTransaction` +> already encodes the receiver, the actions (including any function-call gas/deposit and +> storage deposit), the signer's **nonce**, and a recent `blockHash`. Do **NOT** call +> `account.signAndSendTransaction({ receiverId, actions })` and reconstruct the transaction +> from those JSON fields — that re-derives the nonce and block hash from chain state and +> signs a *different* transaction. Reconstruct the `Transaction` object from the returned +> payload exactly, sign **those bytes**, and submit. + +> **This is an outline, not a drop-in snippet.** The exact `near-api-js` decode path depends +> on the wire shape the API returns (a borsh-serializable `Transaction` object vs. base64 +> bytes). Do not invent the field mapping — follow the reference signer in the Yield.xyz +> signers repo (https://github.com/stakekit/signers) and the +> [`near-api-js` docs](https://github.com/near/near-api-js). + +Outline of the flow: + +1. **Decode the returned payload.** `const txData = JSON.parse(tx.unsignedTransaction);` + Reconstruct a `near-api-js` `transactions.Transaction` from the payload **as-is** — + keeping the API's `nonce`, `blockHash`, `receiverId`, `actions`, and `publicKey`. Do not + re-fetch the nonce or access key from the RPC. +2. **Serialize and hash.** Borsh-serialize the `Transaction` (`utils.serialize.serialize`) + and hash it (`sha256`) to get the bytes to sign. +3. **Sign those bytes** with the account's ed25519 key (`KeyPair.fromString(privateKey)` → + `keyPair.sign(hash)`), and assemble a `SignedTransaction` from the original transaction + plus the signature — without mutating any field. +4. **Broadcast** the `SignedTransaction` via `provider.sendTransaction(...)` (e.g. a + `JsonRpcProvider` against `https://rpc.mainnet.near.org`) and read `result.transaction.hash`. +5. **Submit hash — MANDATORY:** + `await sdk.api.submitTransactionHash(tx.id, { hash: result.transaction.hash });` ## Common Gotchas 1. **Account model**: NEAR uses named accounts (e.g., `alice.near`), not hex addresses. -2. **Storage deposit**: Some NEAR staking operations require a storage deposit. The API includes this in the transactions. +2. **Storage deposit**: Some NEAR staking operations require a storage deposit. The API + includes this in the returned actions — sign them as-is, don't add or drop actions. + +3. **Nonce + blockHash are baked in**: The returned transaction carries the access-key nonce + and a recent block hash. Re-deriving them (which `signAndSendTransaction` does) desyncs the + signed bytes from what the API expects. Sign the payload verbatim. -3. **4-epoch unbonding**: NEAR native staking has a ~52-hour unbonding period (4 epochs). +4. **4-epoch unbonding**: NEAR native staking has a ~52-hour unbonding period (4 epochs). ## Available Yields diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md index c5c8d9a..0844184 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/stellar.md @@ -1,47 +1,110 @@ # Stellar Integration Guide +Covers: Stellar (XLM) and Soroban-based lending pools (Blend / YieldBlox on USDC, XLM, EURC). + ## unsignedTransaction Format -**Encoding:** Base64-encoded XDR transaction envelope -**Parse before signing:** No — base64 decode and use Stellar SDK +**Encoding:** Base64-encoded XDR transaction envelope (string) +**Parse before signing:** No — base64 XDR is decoded by the Stellar SDK directly via `TransactionBuilder.fromXDR(...)`. Do NOT rebuild the transaction. + +The current Stellar yields are **Blend lending pools**, which are **Soroban smart-contract +invocations**. The XDR envelope the API returns already carries the contract call, the +sequence number, the fee, and the Soroban resource footprint / authorization — all resolved +via simulation server-side. You sign it verbatim rather than re-simulating or rebuilding it. ## Required Arguments +Confirm the exact required fields via `mechanics.arguments.enter.fields[]` and +`mechanics.arguments.exit.fields[]` in the yield DTO — do not assume. + | Argument | Required | Description | |----------|----------|-------------| -| `amount` | Yes | Human-readable string. `"100"` = 100 XLM | +| `amount` | Yes | Human-readable string. `"100"` = 100 XLM (or 100 USDC, etc.) | + +For the Blend lending yields (`stellar-usdc-blend-lending-yieldblox-pool`, +`stellar-xlm-blend-lending-fixed-pool-v2`, and siblings), both **enter** and **exit** require +only `amount` and `requiresValidatorSelection=false`. There is no validator selection on +these yields. Still read `fields[]` per-yield, since a yield can expose optional fields. ## Signing +> **Sign the API's XDR envelope VERBATIM.** The base64 XDR already embeds the Soroban +> contract call, the fee, the sequence number, and the resource/authorization footprint +> computed by server-side simulation. Decode it, **sign it as-is, and submit** — do **NOT** +> re-run `prepareTransaction` / re-simulate, re-fetch the account sequence, or rebuild the +> operations. Re-simulating can change the footprint and produce a transaction that no longer +> matches what the API expects. + +Blend yields are Soroban invocations, so submit through the **Soroban RPC server** +(`rpc.Server`), not Horizon — Horizon's `submitTransaction` does not handle Soroban resource +fees the same way. + ```typescript -import * as StellarSdk from "stellar-sdk"; +import { + TransactionBuilder, + Keypair, + Networks, + rpc, +} from "@stellar/stellar-sdk"; -const server = new StellarSdk.Horizon.Server("https://horizon.stellar.org"); -const keypair = StellarSdk.Keypair.fromSecret(secretKey); +const server = new rpc.Server("https://soroban-rpc.mainnet.stellar.gateway.fm"); +const keypair = Keypair.fromSecret(SECRET_KEY); for (const tx of action.transactions) { - // Decode the XDR envelope - const transaction = StellarSdk.TransactionBuilder.fromXDR( + // Decode the base64 XDR envelope VERBATIM — do not re-simulate or rebuild. + const transaction = TransactionBuilder.fromXDR( tx.unsignedTransaction, - StellarSdk.Networks.PUBLIC + Networks.PUBLIC, ); - // Sign + // Sign the decoded transaction as-is. transaction.sign(keypair); - // Broadcast - const result = await server.submitTransaction(transaction); - - // Submit hash — MANDATORY - await sdk.api.submitTransactionHash(tx.id, { hash: result.hash }); + // Submit via Soroban RPC. sendTransaction returns { hash, status } — status is "PENDING". + const sent = await server.sendTransaction(transaction); + if (sent.status === "ERROR") { + throw new Error(`Stellar submit failed: ${JSON.stringify(sent.errorResult)}`); + } + + // Poll getTransaction(hash) until it is no longer NOT_FOUND. + let result = await server.getTransaction(sent.hash); + while (result.status === "NOT_FOUND") { + await new Promise((r) => setTimeout(r, 1000)); + result = await server.getTransaction(sent.hash); + } + if (result.status !== "SUCCESS") { + throw new Error(`Stellar tx failed on-chain: ${result.status}`); + } + + // Submit hash back to Yield.xyz — MANDATORY (the hash is from sendTransaction). + await sdk.api.submitTransactionHash(tx.id, { hash: sent.hash }); + + // Wait for confirmation before the next stepIndex. } ``` +If you encounter a plain (non-Soroban) XLM payment-style envelope, that one can be submitted +to Horizon via `new Horizon.Server(...).submitTransaction(transaction)`, whose response +carries `.hash`. When in doubt about the exact submission path, follow the reference signer in +the Yield.xyz signers repo ([github.com/stakekit/signers](https://github.com/stakekit/signers)) +rather than guessing. + ## Common Gotchas -1. **XDR format**: Stellar uses XDR (External Data Representation) for transaction serialization. +1. **Soroban vs classic submission**: The Blend lending yields are Soroban contract calls — + submit them through `rpc.Server.sendTransaction`, not Horizon. Submitting a Soroban + envelope to Horizon can fail on resource-fee handling. + +2. **Don't re-simulate**: The API simulates and bakes the Soroban resource footprint into the + XDR. Calling `prepareTransaction` / re-simulating yourself can change the footprint and + invalidate the action. Sign the bytes verbatim. -2. **Network passphrase**: Use `StellarSdk.Networks.PUBLIC` for mainnet, `StellarSdk.Networks.TESTNET` for testnet. +3. **Network passphrase**: Use `Networks.PUBLIC` for mainnet, `Networks.TESTNET` for testnet. + A wrong passphrase changes the signed hash and the submit fails. + +4. **Hash comes from sendTransaction**: `sendTransaction` returns the `hash` immediately + (status `PENDING`). Use that hash for both `getTransaction` polling and `submit-hash` — do + not wait for a separate hash from `getTransaction`. ## Available Yields @@ -49,3 +112,11 @@ for (const tx of action.transactions) { curl "https://api.yield.xyz/v1/yields?network=stellar" \ -H "x-api-key: YOUR_KEY" ``` + +Common yieldIds (Blend lending pools): +- `stellar-usdc-blend-lending-yieldblox-pool` +- `stellar-xlm-blend-lending-yieldblox-pool` +- `stellar-eurc-blend-lending-yieldblox-pool` +- `stellar-usdc-blend-lending-fixed-pool-v2` +- `stellar-xlm-blend-lending-fixed-pool-v2` +- `stellar-eurc-blend-lending-fixed-pool-v2` diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md index 543f9b5..32e931b 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/substrate.md @@ -45,34 +45,32 @@ All chain-specific arguments are flat keys inside `arguments` (no `additionalAdd ## Signing -```typescript -import { ApiPromise, WsProvider } from "@polkadot/api"; -import { Keyring } from "@polkadot/keyring"; - -const provider = new WsProvider("wss://rpc.polkadot.io"); -const api = await ApiPromise.create({ provider }); -const keyring = new Keyring({ type: "sr25519" }); -const account = keyring.addFromUri("//Alice"); - -for (const tx of action.transactions) { - const payload = JSON.parse(tx.unsignedTransaction); - - // Reconstruct the extrinsic and sign the full payload (era, nonce, blockHash, ...), - // not just the method bytes — otherwise the signature is invalid. - const extrinsic = api.createType("Extrinsic", payload.method); - const signingPayload = api.createType("ExtrinsicPayload", payload, { - version: extrinsic.version, - }); - const { signature } = signingPayload.sign(account); - extrinsic.addSignature(account.address, signature, signingPayload.toHex()); - - // Broadcast - const hash = await api.rpc.author.submitExtrinsic(extrinsic); - - // Submit hash — MANDATORY - await sdk.api.submitTransactionHash(tx.id, { hash: hash.toHex() }); -} -``` +> **This is an outline, not a drop-in snippet.** Substrate extrinsic signing is fiddly: +> you must sign the **exact** payload the API returned (`method`, `era`, `nonce`, `tip`, +> `specVersion`, `transactionVersion`, `genesisHash`, `blockHash`) over the SCALE-encoded +> `ExtrinsicPayload`, then attach the signature to the call — **do NOT rebuild the call or +> re-derive era/nonce from chain state**, or the signature will not verify. The precise +> `@polkadot/api` / `@polkadot/util-crypto` calls depend on the metadata version of the +> target chain, so follow the reference signer in the Yield.xyz signers repo: +> https://github.com/stakekit/signers + +> **Sign the API's transaction VERBATIM — don't rebuild it.** Every field above is part of +> what gets signed. Re-fetching the nonce, regenerating the era, or reconstructing the call +> from the `method` bytes alone changes the signed payload and the broadcast fails. + +Outline of the flow: + +1. **Decode the returned payload.** `const payload = JSON.parse(tx.unsignedTransaction);` + This carries the SCALE-encoded `method` plus the signed-extra fields (`era`, `nonce`, + `tip`, `specVersion`, `transactionVersion`, `genesisHash`, `blockHash`). +2. **Sign the payload as-is** with the account's sr25519 (Polkadot/Bittensor default) or + ed25519 keypair — sign the SCALE-encoded `ExtrinsicPayload` built from the fields above, + not just `method`. See the signers repo for the exact construction per chain metadata. +3. **Attach the signature** to the extrinsic (signer address + signature + the signed + payload), producing the final signed extrinsic — without mutating any of the signed fields. +4. **Submit** the signed extrinsic via `author.submitExtrinsic` (or the chain's RPC) and read + the returned tx hash. +5. **Submit hash — MANDATORY:** `await sdk.api.submitTransactionHash(tx.id, { hash });` ## Common Gotchas diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md index bcb4f8a..16ac7a2 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/sui.md @@ -2,48 +2,88 @@ ## unsignedTransaction Format -**Encoding:** Base64-encoded BCS transaction bytes -**Parse before signing:** No — base64 decode and pass to Sui SDK +**Encoding:** Base64-encoded BCS transaction bytes (string) +**Parse before signing:** No — base64-decode to the BCS bytes and sign those bytes directly with the Sui SDK. Do NOT rebuild a `Transaction`. + +The API returns the fully-built transaction (object references, gas object, and budget all +resolved) as base64 BCS. Sui is **object-centric** and requires explicit gas objects — both +are already baked into the bytes, so you sign them verbatim rather than reconstructing the +transaction client-side. ## Required Arguments +Confirm the exact required fields via `mechanics.arguments.enter.fields[]` and +`mechanics.arguments.exit.fields[]` in the yield DTO — enter and exit differ. + | Argument | Required | Description | |----------|----------|-------------| -| `amount` | Yes | Human-readable string. `"10"` = 10 SUI | +| `amount` | Yes (enter) | Human-readable string. `"10"` = 10 SUI | | `validatorAddress` | Yes (native staking) | Get from `GET /v1/yields/{id}/validators` | +Note: for `sui-sui-native-staking`, **enter** requires `amount` + `validatorAddress`, while +**exit** (unstake) requires only `validatorAddress` — there is no `amount` on exit. Always +read the per-yield `fields[]` rather than assuming the two sides match. + ## Signing +> **Sign the API's transaction bytes VERBATIM.** The base64 BCS the API returns already +> resolves the gas object, gas budget, and all object references. Decode it to bytes and sign +> **those exact bytes** — do **NOT** deserialize-then-rebuild a `Transaction`, re-resolve gas, +> or change the object references. Sui commits the signature to the Blake2b digest of the +> intent message over these bytes; signing different bytes makes the execute fail. + +> **SDK version:** this flow targets `@mysten/sui` **v1.x** — `getFullnodeUrl` (from +> `@mysten/sui/client`) and `client.executeTransactionBlock(...)` were reorganized in v2 +> (the current npm `latest`), so a fresh `npm i @mysten/sui` will break this snippet. Pin +> `@mysten/sui@^1.36.0`, or adapt the imports/calls for v2. + ```typescript import { SuiClient, getFullnodeUrl } from "@mysten/sui/client"; import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; -import { Transaction } from "@mysten/sui/transactions"; const client = new SuiClient({ url: getFullnodeUrl("mainnet") }); -const keypair = Ed25519Keypair.fromSecretKey(privateKey); +const keypair = Ed25519Keypair.fromSecretKey(PRIVATE_KEY); for (const tx of action.transactions) { + // Decode the base64 BCS transaction bytes — sign these VERBATIM. const txBytes = Buffer.from(tx.unsignedTransaction, "base64"); - // Sign - const signature = await keypair.signTransaction(txBytes); + // signTransaction returns { bytes, signature } where: + // - `bytes` is the base64 of exactly what was signed (the same tx bytes) + // - `signature` commits to the intent message over those bytes + const { bytes, signature } = await keypair.signTransaction(txBytes); - // Execute + // Execute the SIGNED bytes (not the raw string) with the matching signature. const result = await client.executeTransactionBlock({ - transactionBlock: tx.unsignedTransaction, - signature: signature.signature, + transactionBlock: bytes, + signature, + options: { showEffects: true }, }); - // Submit hash — MANDATORY + // The transaction digest IS the hash on Sui. + // Submit hash back to Yield.xyz — MANDATORY await sdk.api.submitTransactionHash(tx.id, { hash: result.digest }); + + // Wait for confirmation before the next stepIndex. + await client.waitForTransaction({ digest: result.digest }); } ``` ## Common Gotchas -1. **Object model**: Sui uses an object-centric model. The API handles object references internally. +1. **Sign the bytes, execute the bytes**: A common bug is to sign the decoded bytes but then + pass the raw `tx.unsignedTransaction` string back to `executeTransactionBlock`. Pass the + `bytes` returned by `signTransaction` (or the same base64 you signed) together with the + `signature` — the two must correspond to the same bytes. + +2. **Object model**: Sui uses an object-centric model. The API resolves object references for + you — never re-resolve or substitute them, or the signed bytes won't match on-chain state. + +3. **Gas objects**: Sui requires explicit gas objects and a gas budget. Both are included in + the transaction the API returns. Don't re-pick a gas coin or change the budget. -2. **Gas objects**: Sui requires explicit gas objects. The API includes gas configuration in the transaction. +4. **Digest is the hash**: `executeTransactionBlock` returns `result.digest` — that is the + transaction hash you submit to `submit-hash`. There is no separate hash field. ## Available Yields @@ -53,4 +93,4 @@ curl "https://api.yield.xyz/v1/yields?network=sui" \ ``` Common yieldIds: -- `sui-sui-native-staking` +- `sui-sui-native-staking` (native staking; `requiresValidatorSelection=true`; enter `amount` + `validatorAddress`, exit `validatorAddress`) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md index 7687379..ebc84cd 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/integration-patterns.md @@ -62,7 +62,7 @@ The `@stakekit/widget` is a pre-built React component that handles the entire yi flow — discovery, entry, exit, and management. No need to call the API directly. ```tsx -import "@stakekit/widget/package/css"; +import "@stakekit/widget/style.css"; import { SKApp, darkTheme } from "@stakekit/widget"; function YieldPage() { @@ -84,7 +84,7 @@ pass the **`externalProviders`** prop. This **skips the connection step entirely widget uses the address and signer you supply instead of asking the user to connect. ```tsx -import "@stakekit/widget/package/css"; +import "@stakekit/widget/style.css"; import { SKApp, darkTheme } from "@stakekit/widget"; { - // skTx is a discriminated union by chain: { type: "evm" | "solana" | "ton" | "tron", tx } + // skTx is a discriminated union by chain: { type: "evm" | "solana" | "ton" | "tron" | "bittensor", tx } // txMeta carries { txId, txType, actionId, actionType, amount, inputToken, providersDetails } const txHash = await myInfra.signAndBroadcast(skTx); return { type: "success", txHash }; // or { type: "error", error } or just the hash string @@ -121,8 +121,12 @@ Key points for this path: handles the submit-hash/tracking for you. You never hand keys to the widget. - `currentAddress` is the address the widget operates on; update it (re-render with a new value) when your wallet switches accounts. -- `skTx` is already decoded per chain (`evm`/`solana`/`ton`/`tron`) — sign it as-is; don't - mutate it (same rule as raw `unsignedTransaction`). +- `skTx` is already decoded per chain (`evm`/`solana`/`ton`/`tron`/`bittensor`) — sign it + as-is; don't mutate it (same rule as raw `unsignedTransaction`). +- **Theming:** `SKApp` accepts a `theme` prop (a `ThemeWrapperTheme` object — use the + exported `darkTheme`/`lightTheme`, or supply a custom theme to match your brand) plus + `tokenIconMapping`/`chainIconMapping` to override token/chain icons. The widget is more + customizable than "you get the default UI" implies — restyle it instead of forking. - Related props: `disableInjectedProviderDiscovery` (stop the widget probing for injected wallets when you supply your own), `validatorsConfig` (allow/block/prefer validators per chain), and `mapWalletListFn`/`mapWalletFn` (customize the connector list *if* you do use diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md index fc7a0f9..697742f 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/mcp-tools.md @@ -43,7 +43,7 @@ Fetches the **live** Yield OpenAPI spec (`https://api.yield.xyz/docs.json`, defa - **Use when:** user says "I'm getting a 422", "400 error", "rate limited", "transaction failed", "FAILED status", "why did this fail", or pastes any error code / message from the API. ### `yield_list_repos` -- Lists the public StakeKit/Yield.xyz GitHub repos that serve as **working code references** for integrations — the staking/yield widget (and its Next.js / Vite reference apps), the TypeScript/JS SDKs, runnable `api-recipes`, the `signers` and `shield` transaction-security libraries, and an `assets` repo for token/provider logos. Returns each repo with a one-line description and the raw-file fetch pattern (`https://raw.githubusercontent.com/stakekit///`). +- Lists the public Yield.xyz GitHub repos that serve as **working code references** for integrations — the staking/yield widget (and its Next.js / Vite reference apps), the TypeScript/JS SDKs, runnable `api-recipes`, the `signers` and `shield` transaction-security libraries, and an `assets` repo for token/provider logos. Returns each repo with a one-line description and the raw-file fetch pattern (`https://raw.githubusercontent.com/stakekit///`). - **Use when:** the other doc tools don't fully resolve an integration problem and you need to read real, working source code — e.g. "how does the widget wire wallet connect", "show me a runnable enter-position example", "how does Shield validate a transaction". It points at code; it never returns live data — use the REST API for that. @@ -124,22 +124,9 @@ Hit an error? → yield_troubleshoot_error ``` **Static "how-to-build" needs → read this skill's `references/` files (no tool call):** -``` -Sort/search/filter params for /v1/yields, - or yield-type mechanics? → references/yield-types.md -Transaction lifecycle / signing / - stepIndex / submit-hash? → references/transaction-lifecycle.md -unsignedTransaction format & signing - SDK for a specific chain? → references/signing-patterns.md - → references/chains/.md -Safety rules / pre-execution checks / - guardrails / policies? → references/policies.md -Rate limits / 429 / key tiers? → references/api-limits.md -Which integration approach (widget / - SDK / REST / programmatic)? → references/setup.md -Integration architecture patterns? → references/integration-patterns.md -Field naming / common mistakes? → references/api-field-mapping.md, references/common-pitfalls.md -``` +For the full need → file mapping (yield types, transaction lifecycle, signing, safety, +rate limits, integration approach, architecture, field naming), see the canonical router: +**SKILL.md → Reference Files**. **Data / execution:** ``` @@ -157,17 +144,10 @@ User wants to actually run/test an ## Static Guidance — read from this skill's reference files -These topics are **not** MCP tools. Read the file directly when you need the guidance. - -| Need | Read | -|---|---| -| `/v1/yields` query params + the 8 yield types & mechanics | `references/yield-types.md` | -| End-to-end transaction flow (discover → sign → submit-hash → confirm) | `references/transaction-lifecycle.md` | -| Per-chain `unsignedTransaction` format & signing SDK | `references/signing-patterns.md` + `references/chains/.md` | -| Safety rules, pre-execution checks, guardrails | `references/policies.md` | -| Rate limits, key tiers, throttling | `references/api-limits.md` | -| Choosing an integration approach (widget / SDK / REST / programmatic) | `references/setup.md` | -| Integration architecture patterns by product type | `references/integration-patterns.md` | +Static "how-to-build" topics (yield types, transaction lifecycle, signing, safety, +rate limits, integration approach, architecture patterns) are **not** MCP tools — they +live in this skill's `references/*.md` files. For the full need → file mapping, use the +canonical router: **SKILL.md → Reference Files**. Don't duplicate that table here. --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md index 8aefa40..9485abb 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/output-formats.md @@ -43,6 +43,11 @@ Filter out low-TVL yields before displaying: | BTC / wrapped BTC | $500K | | Other tokens | $100K | +> **Note:** `GET /v1/yields` list items carry **no current-`tvl` field** — you cannot filter +> the list response on TVL directly. To apply these thresholds, fetch TVL per yield from the +> `/v1/yields/{id}/tvl/history` endpoint and filter client-side. Don't filter on a `tvl` +> property that isn't on list items. + --- ## Transaction Summary diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md index f39d6d4..b8c5d9d 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/policies.md @@ -1,48 +1,10 @@ -# API Usage Policies +# Safety Rules & Guardrails -Guidelines for efficient API usage in Yield.xyz integrations. +Pre-execution checks, safety rules, and guardrail configuration for Yield.xyz +integrations. ---- - -## Rate Limits - -- Default rate limit depends on your API key tier (trial/standard/pro) -- If you receive `429 Too Many Requests`, respect the `retry-after` header -- Implement exponential backoff for retries - ---- - -## Caching Recommendations - -| Data Type | Cache Duration | Notes | -|---|---|---| -| Yield list (`/v1/yields`) | 5-15 minutes | APYs update periodically | -| Yield metadata (`/v1/yields/{id}`) | 5-15 minutes | Schema rarely changes | -| Validators | 15-30 minutes | Validator set changes slowly | -| Balances | Do not cache | Always fetch fresh | -| Networks | 1 hour | Rarely changes | - ---- - -## Pagination - -- Default `limit`: 20 -- Maximum `limit`: **100** — a `limit` greater than 100 returns **HTTP 400** -- Use `offset` for pagination — pagination is **offset-only**, there is no cursor -- The response envelope is `{ items, total, offset, limit }` — the array key is **`items`** (not `data`) -- Do not attempt to fetch all yields in a single request - ---- - -## Best Practices - -1. **Fetch only what you need.** Use query parameters (`network`, `token`, `type`) to filter server-side -2. **Cache yield metadata.** The schema and mechanics don't change between requests -3. **Don't poll balances excessively.** Check after user actions, not on a tight loop -4. **Use the SDK for TypeScript.** It handles pagination, typing, and error handling -5. **Log all submit-hash calls.** If a hash submission fails, positions won't update — you need to retry -6. **Handle 503 gracefully.** Upstream protocols can be temporarily unavailable -7. **Set a 3-second timeout** on all API calls to avoid hanging requests +> For **rate limits, key tiers, throttling, caching, and pagination**, see +> [`api-limits.md`](./api-limits.md). This file covers safety only. --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/scaffold.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/scaffold.md new file mode 100644 index 0000000..2d0317e --- /dev/null +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/scaffold.md @@ -0,0 +1,197 @@ +# Project Scaffolds (greenfield) + +Canonical starting skeletons for each primary greenfield path. Pick the one that matches +the use case (see `setup.md` → "Choosing your integration approach"), scaffold it, then +wire real calls. + +These are **starting skeletons, not full apps** — just enough to render/respond and prove +the API key works. + +--- + +## 1. Widget app (default greenfield path) + +Vite + React + TypeScript + `@stakekit/widget`. Fastest path to a running yield UI. + +``` +my-yield-app/ + index.html + package.json + .env + src/ + main.tsx + App.tsx +``` + +**`package.json`** (deps — note React 19, see `common-pitfalls.md` #14): + +```jsonc +{ + "scripts": { "dev": "vite", "build": "vite build" }, + "dependencies": { + "@stakekit/widget": "latest", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.0", + "typescript": "^5.5.0", + "vite": "^5.4.0" + } +} +``` + +**`.env`** (Vite exposes only `VITE_`-prefixed vars to the client): + +```bash +VITE_YIELD_API_KEY=your_api_key_here +``` + +**`index.html`**: + +```html + + + +
+ + + +``` + +**`src/main.tsx`**: + +```tsx +import { createRoot } from "react-dom/client"; +import App from "./App"; + +createRoot(document.getElementById("root")!).render(); +``` + +**`src/App.tsx`** — mount `SKApp`: + +```tsx +import "@stakekit/widget/style.css"; +import { SKApp, darkTheme } from "@stakekit/widget"; + +export default function App() { + return ( + + ); +} +``` + +**Run:** `npm install && npm run dev` + +--- + +## 2. Custom TS app + +Next.js (App Router) + `@yieldxyz/sdk`. For a custom UI over typed SDK calls. + +``` +my-yield-app/ + package.json // deps: next, react ^19, react-dom ^19, @yieldxyz/sdk + .env.local + lib/ + yield.ts // sdk.configure() once, server-side + app/ + page.tsx // server component, reads yields +``` + +**`.env.local`**: + +```bash +YIELD_API_KEY=your_api_key_here +``` + +**`lib/yield.ts`** — configure the singleton once (server-only; never expose the key to the client): + +```ts +import { sdk } from "@yieldxyz/sdk"; + +sdk.configure({ apiKey: process.env.YIELD_API_KEY! }); + +export { sdk }; +``` + +**`app/page.tsx`** — server component calling the SDK: + +```tsx +import { sdk } from "@/lib/yield"; + +export default async function Page() { + const yields = await sdk.api.getYields({ network: "ethereum" }); + return
{JSON.stringify(yields, null, 2)}
; +} +``` + +**Run:** `npm install && npm run dev` + +--- + +## 3. Backend (non-JS) — Python FastAPI + +Minimal REST skeleton for any non-JS backend. Signing happens server-side via +`eth-account` — see `signing-patterns.md` (EVM — Server-Side Signing) for the seam. + +``` +my-yield-backend/ + main.py + .env + requirements.txt +``` + +**`requirements.txt`**: + +``` +fastapi +uvicorn +requests +eth-account +web3 +``` + +**`.env`**: + +```bash +YIELD_API_KEY=your_api_key_here +``` + +**`main.py`** — one endpoint proxying yield discovery: + +```python +import os +import requests +from fastapi import FastAPI + +app = FastAPI() +API_KEY = os.environ["YIELD_API_KEY"] + +@app.get("/yields") +def list_yields(network: str = "ethereum"): + resp = requests.get( + "https://api.yield.xyz/v1/yields", + params={"network": network}, + headers={"x-api-key": API_KEY}, + timeout=3, # 3s max on external calls + ) + resp.raise_for_status() + return resp.json() + +# Signing (enter/exit actions) is a separate seam: build the action via +# POST /v1/actions/enter, then sign each tx with eth-account / web3. +# See signing-patterns.md. +``` + +**Run:** `pip install -r requirements.txt && uvicorn main:app --reload` + +--- + +These are starting skeletons — confirm field names against the live spec +(api.yield.xyz/docs.json) before wiring real calls. diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md index 264076c..6a9fffd 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/setup.md @@ -2,61 +2,18 @@ ## Prerequisites -- An MCP-compatible AI agent (Claude Code, Codex, Gemini CLI, etc.) - A Yield.xyz API key (get one at https://dashboard.yield.xyz/login) - Node.js 18+ (for SDK usage) or any HTTP client (for REST API) +- Optional: an MCP-compatible AI agent (Claude Code, Codex, Gemini CLI, etc.) if you + want to use the build-time doc tools described in the optional step below ---- - -## Step 1 — Register the Yield.xyz MCP Server (REQUIRED — do this first) - -**This step is mandatory, not optional.** The builder skill depends on the live tools -exposed by the `yield-agentkit` MCP server (`yield_get_api_spec`, `yield_lookup_docs`, -`yield_fetch_doc`, `yield_troubleshoot_error`, `yield_list_repos`). Without the MCP -registered, the skill cannot fetch the live OpenAPI spec, search/read the live docs, or -diagnose API errors against the live spec — so code generation will fall back on -stale/hallucinated information. (Chain-specific signing, transaction lifecycle, yield -types, and safety guidance live in this skill's own `references/` files.) - -**The skill MUST auto-configure the MCP for the user as the very first action** when -the skill is invoked (before asking for an API key, before anything else). - -### Automatic configuration - -Run the correct command for the user's agent: - -```bash -# Claude Code -claude mcp add yield-agentkit --transport http https://mcp.yield.xyz/mcp - -# Codex, Gemini CLI, or any agent using an MCP config file — write to ~/.mcp.json -# or the project-local .mcp.json: -# { -# "mcpServers": { -# "yield-agentkit": { -# "command": "npx", -# "args": ["-y", "mcp-remote", "https://mcp.yield.xyz/mcp"] -# } -# } -# } -``` - -### Verify registration - -After registering, confirm the server is connected: - -```bash -claude mcp list -``` - -The output should include `yield-agentkit` with status `✓ Connected`. If it fails, -re-run the add command or inspect `.mcp.json` for typos. - -**Do not proceed to Step 2 until the MCP is registered and connected.** +The production integration calls `https://api.yield.xyz` directly — via the +`@yieldxyz/sdk` or REST/curl. That's the real starting point. The MCP doc tools are +only a build-time aid; nothing you ship depends on them. --- -## Step 2 — Get a Yield.xyz API Key +## Step 1 — Get a Yield.xyz API Key If you don't have one yet: 1. Go to https://dashboard.yield.xyz/login @@ -72,7 +29,7 @@ YIELD_API_KEY=your_api_key_here --- -## Step 3 — Verify API Access +## Step 2 — Verify API Access Test that your key works: @@ -99,7 +56,7 @@ and only switch the network id to mainnet once the flow works end-to-end. --- -## Step 4 — Install SDK (optional) +## Step 3 — Install SDK (optional) For TypeScript/JavaScript projects, the SDK provides typed wrappers: @@ -116,10 +73,60 @@ The full OpenAPI spec is at `https://api.yield.xyz/docs.json`. --- +## Optional — Register the Yield.xyz MCP Server (build-time reference) + +This is an optional convenience, not a requirement. The `yield-agentkit` MCP server +exposes doc tools (`yield_get_api_spec`, `yield_lookup_docs`, `yield_fetch_doc`, +`yield_troubleshoot_error`, `yield_list_repos`) that let an AI agent ground generated +code against the live OpenAPI spec and docs **while building**. It is a build-time +reference only — nothing you ship calls the MCP, and it is not a runtime dependency. + +If you skip it, the same information is available by fetching the live spec directly: + +```bash +curl -s https://api.yield.xyz/docs.json | jq . +``` + +(Chain-specific signing, transaction lifecycle, yield types, and safety guidance all +live in this skill's own `references/` files regardless.) + +To register the doc tools, run the correct command for your agent: + +```bash +# Claude Code +claude mcp add yield-agentkit --transport http https://mcp.yield.xyz/mcp + +# Codex, Gemini CLI, or any agent using an MCP config file — write to ~/.mcp.json +# or the project-local .mcp.json: +# { +# "mcpServers": { +# "yield-agentkit": { +# "command": "npx", +# "args": ["-y", "mcp-remote", "https://mcp.yield.xyz/mcp"] +# } +# } +# } +``` + +To confirm it's connected: + +```bash +claude mcp list +``` + +The output should include `yield-agentkit` with status `✓ Connected`. If it fails, +re-run the add command or inspect `.mcp.json` for typos. + +--- + ## Choosing your integration approach -Pick the integration approach that matches the use case. When in doubt, default to the -TypeScript SDK. +Pick the integration approach that matches the use case. There is no single flat +default — match the situation: + +- **Greenfield / unspecified staking app** → `@stakekit/widget` (fastest to a running app) +- **Existing React/TS app needing custom UI** → TypeScript SDK +- **Non-JS (Python, Go, …)** → REST directly ### Widget Component (`@stakekit/widget`) @@ -140,7 +147,7 @@ transaction signing, and position tracking. ```tsx npm install @stakekit/widget // requires React 19+ — see common-pitfalls.md #14 -import "@stakekit/widget/package/css"; +import "@stakekit/widget/style.css"; import { SKApp, darkTheme } from "@stakekit/widget"; function YieldPage() { @@ -156,14 +163,15 @@ The React component is `SKApp`. For a non-React app, use the bundled build: - [widget repo](https://github.com/stakekit/widget) - [Widget Documentation](https://docs.yield.xyz/docs/widget) -### TypeScript SDK (`@yieldxyz/sdk`) — Recommended Default +### TypeScript SDK (`@yieldxyz/sdk`) — Recommended for custom TypeScript/JS apps Typed SDK with methods for all API endpoints. Handles auth, request formatting, and -type safety. The best developer experience for most use cases. +type safety. The best developer experience when you're building custom UI in a +TypeScript/JavaScript app. - **Best for:** Consumer wallets, frontend apps, mobile apps, and any - TypeScript/JavaScript project that wants typed access. Start here unless you have a - specific reason to use something else. + TypeScript/JavaScript project that wants typed access and a custom UI (when the + prebuilt widget isn't a fit). - **Tradeoffs:** TypeScript/JavaScript only. Covers all endpoints. Good balance of control and convenience. For other languages, use the REST API directly. - **Match signals:** sdk, typescript, npm, typed, client, frontend, wallet, consumer, @@ -236,7 +244,12 @@ curl -X POST "https://api.yield.xyz/v1/actions/enter" \ - **Resources:** - [API Reference](https://docs.yield.xyz/reference/getting-started-with-your-api) - [Core Concepts](https://docs.yield.xyz/docs/core-concepts) - - [API Recipes](https://github.com/stakekit/api-recipes) — runnable REST + ethers.js examples (no SDK) + - **Non-JS (Python/Go/…) on-ramp:** follow the language-agnostic REST flow in + `references/transaction-lifecycle.md`, then sign with the Python block in + `references/signing-patterns.md` (EVM — Server-Side Signing). + - [API Recipes](https://github.com/stakekit/api-recipes) — TypeScript/ethers.js + reference (no SDK), useful for understanding the raw REST calls. Note: TS only, not + a Python on-ramp. ### Programmatic Access API + REST API @@ -270,7 +283,7 @@ A single index of everything this skill recommends, with links. **Repos** - SDK — [github.com/stakekit/sdk](https://github.com/stakekit/sdk) - Widget — [github.com/stakekit/widget](https://github.com/stakekit/widget) -- API recipes (runnable REST + ethers.js examples) — [github.com/stakekit/api-recipes](https://github.com/stakekit/api-recipes) +- API recipes (TypeScript/ethers.js REST reference — TS only, not a Python on-ramp) — [github.com/stakekit/api-recipes](https://github.com/stakekit/api-recipes) - Shield (transaction-security validation) — [github.com/stakekit/shield](https://github.com/stakekit/shield) - Signers (signing helpers) — [github.com/stakekit/signers](https://github.com/stakekit/signers) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md index 607931d..87c5c57 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/signing-patterns.md @@ -112,9 +112,23 @@ For backends, custody platforms, and any environment where you control the priva - `receipt.wait()` (ethers) or `waitForTransactionReceipt()` (viem) works correctly server-side **Python (`eth-account` + `web3.py`):** `eth-account` requires two key-shape adaptations -before signing — drop the `from` key and rename `gasLimit` → `gas` (neither changes a -value). Sign with `Account.from_key(...).sign_transaction(tx)`, then broadcast -`signed.raw_transaction`. Full example: `references/chains/evm.md` ("EVM signing — Python"). +before signing — drop the `from` key and rename `gasLimit` → `gas`. Both are key-shape +adaptations to satisfy `eth-account`, **NOT** modifications to the transaction: do not +touch `to` / `data` / `value` / amounts, and do not change the gas values themselves. + +```python +from eth_account import Account +from web3 import Web3 + +tx = json.loads(unsigned_transaction) # EVM unsignedTransaction is a JSON string +tx.pop("from", None) # eth-account derives sender from the key; it rejects `from` +tx["gas"] = tx.pop("gasLimit") # eth-account uses `gas`, not `gasLimit` +signed = Account.from_key(PRIVATE_KEY).sign_transaction(tx) # .raw_transaction (eth-account >=0.13); older: .rawTransaction +tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction).hex() +# then PUT /v1/transactions/{id}/submit-hash with { "hash": tx_hash }, or POST .../submit to let Yield broadcast +``` + +`references/chains/evm.md` carries the same example under "EVM signing — Python". --- @@ -222,8 +236,10 @@ Check the yield schema to confirm. ## Validator Object Shape (All Chains) -`GET /v1/yields/{yieldId}/validators` returns an array of validator objects. The -stable fields, identical across every staking chain, are: +`GET /v1/yields/{yieldId}/validators` returns a **paginated envelope** +`{ items: ValidatorDto[], total, limit, offset }` — **not** a bare array. Read the +validators off `.items` (mapping or indexing the raw response directly will crash). +The stable fields on each `ValidatorDto`, identical across every staking chain, are: | Field | Type | Notes | |---|---|---| @@ -236,8 +252,9 @@ stable fields, identical across every staking chain, are: | `providerId` | string | Validator provider id | | `rewardRate` | object | `{ total, rateType: "APR", components }` | -> **There is no `apr` field.** The validator's APR is `rewardRate.total` (a decimal -> fraction; `rateType` is `"APR"`). Per-chain additions: **Polkadot** adds `nominatorCount`; +> **`apr` is null/deprecated — use `rewardRate.total` for the rate.** The validator object +> does carry an `apr` key, but its value is `null`; read the APR from `rewardRate.total` +> (a decimal fraction; `rateType` is `"APR"`). Per-chain additions: **Polkadot** adds `nominatorCount`; > **Bittensor** adds a `subnet {}` object. --- diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md index 4e8e91a..0103c61 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/transaction-lifecycle.md @@ -29,7 +29,7 @@ the action's `executionPattern` (synchronous / asynchronous / batch) decides seq Find available yield opportunities: ```typescript -// REST API — filter + sort; paginate with limit/offset (max limit 100, see yield-types.md) +// REST API — filter + sort; paginate with limit/offset (max limit 100, see api-limits.md) const response = await fetch( "https://api.yield.xyz/v1/yields?network=ethereum&token=USDC&type=staking&sort=rewardRateDesc&limit=100&offset=0", { headers: { "x-api-key": API_KEY } }, @@ -45,7 +45,7 @@ Filter / sort results by: - `token` — token symbol - `type` — yield type (e.g. `staking`, `lending`, `vault`) - `sort` — e.g. `rewardRateDesc` -- `limit` / `offset` — pagination (max `limit` 100; see yield-types.md) +- `limit` / `offset` — pagination (max `limit` 100; see api-limits.md) > There is **no `status.enter` / `status.exit` query filter** (sending `?status.enter=true` > returns **400** "property status.enter should not exist"). To find yields accepting new From 4f10f6570213af9a578bec82577975c8df116927 Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:45:03 +0200 Subject: [PATCH 22/23] docs(builder-skill): route 'building an agent' to the agentic-execution skills Add routing in 'When This Skill Activates': autonomous/agentic execution (own wallet, discovers AND executes, policy-gated signing) -> yield-agentkit-privy / -moonpay / -rwakit-privy; this skill = the integration surface (discovery, tx construction, your own signing infra). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/yield-agentkit-builder/SKILL.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md index 22fbe42..3782293 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/SKILL.md @@ -71,8 +71,14 @@ Activate when the user says things like: - "Set up a neobank with yield features" - "Add yield to my wallet app" -If the user is just exploring yields ("What's the best USDC yield on Base?"), that's -the `yield-agentkit` skill — not this one. +**Route to a different skill when:** +- **Exploring yields conversationally** ("What's the best USDC yield on Base?") → `yield-agentkit`. +- **Building an autonomous agent** — an agent with its own wallet that *discovers **and executes*** yield strategies itself (autonomous or policy-gated signing/broadcasting) → the agentic-execution skills, which carry the wallet, policy, and signing an agent needs: + - `yield-agentkit-privy` — Privy agentic wallets; autonomous & semi-autonomous (approval) strategies; policy enforcement. + - `yield-agentkit-moonpay` — MoonPay wallet auth + signing. + - `yield-agentkit-rwakit-privy` — RWA / permissioned yields on top of Privy. + +This skill builds the **integration surface** — discovery, transaction construction, and wiring *your own* signing infra. Use it for the API/app layer; use the agentic skills for an agent's wallet + autonomous execution. (For one-off execution the user wants to run, see Critical Rule #3.) --- From 4e1774bcb96de3ca280baa21cb657b7cc587ea2b Mon Sep 17 00:00:00 2001 From: Philip <22058587+Philippoes@users.noreply.github.com> Date: Wed, 17 Jun 2026 14:58:50 +0200 Subject: [PATCH 23/23] fix(builder-skill): aptos unsignedTransaction is base64, not hex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The API serializes the Aptos RawTransaction as base64 (serialize-and-estimate-tx.ts), but the signing snippet decoded it as hex via Hex.hexInputToUint8Array — which throws/garbles on base64 input. Decode with Buffer.from(..., "base64") and drop the unused Hex import. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../yield-agentkit-builder/references/chains/aptos.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md index 4f1938f..b1b14b4 100644 --- a/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md +++ b/yield-agentkit-skills/skills/yield-agentkit-builder/references/chains/aptos.md @@ -2,7 +2,7 @@ ## unsignedTransaction Format -**Encoding:** Serialized Aptos `RawTransaction` (BCS bytes, returned as a string) +**Encoding:** Base64-encoded Aptos `RawTransaction` (BCS bytes, returned as a string) **Parse before signing:** No — decode to bytes and deserialize into a `RawTransaction` with the SDK's `Deserializer`. Do NOT rebuild the payload. The transaction the API returns already encodes the Move entry-function call, the sender, @@ -44,16 +44,15 @@ import { RawTransaction, SimpleTransaction, Deserializer, - Hex, } from "@aptos-labs/ts-sdk"; const aptos = new Aptos(new AptosConfig({ network: Network.MAINNET })); const signer = Account.fromPrivateKey({ privateKey: new Ed25519PrivateKey(PRIVATE_KEY) }); for (const tx of action.transactions) { - // tx.unsignedTransaction is a serialized RawTransaction. Deserialize VERBATIM — + // tx.unsignedTransaction is a base64-encoded RawTransaction. Deserialize VERBATIM — // do NOT rebuild the payload from chain state. - const rawTxnBytes = Hex.hexInputToUint8Array(tx.unsignedTransaction); + const rawTxnBytes = Buffer.from(tx.unsignedTransaction, "base64"); const transaction = new SimpleTransaction( RawTransaction.deserialize(new Deserializer(rawTxnBytes)), );