Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions defi-integrations/with-vaultsfyi-api/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
NEXT_PUBLIC_PARA_API_KEY=your-para-api-key
NEXT_PUBLIC_PARA_ENVIRONMENT=BETA
NEXT_PUBLIC_BASE_RPC_URL=https://mainnet.base.org

# Server-side only — read by the /api/vaultsfyi/[...path] proxy route and injected as
# the x-api-key header. Never expose in the browser; do not prefix with NEXT_PUBLIC_.
VAULTS_FYI_API_KEY=your-vaultsfyi-api-key
40 changes: 40 additions & 0 deletions defi-integrations/with-vaultsfyi-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files
.env*.local
.env

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
92 changes: 92 additions & 0 deletions defi-integrations/with-vaultsfyi-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# vaults.fyi Yield API with Para SDK Demo

This project demonstrates how to use the Para SDK as a transaction signer for [vaults.fyi](https://vaults.fyi) API transactions in a NextJS application. It showcases a multi-curator DeFi yield interface that leverages vaults.fyi's coverage of 1,000+ vaults across 82 protocols to provide optimal deposit, tracking, and reward-claim flows on Base.

## Features

- Connect to wallets via Para SDK (email, social, and wallet connections)
- Discover top USDC vaults on Base ranked by 7-day APY across every protocol vaults.fyi indexes
- Deposit directly into any vault with calldata generated by the vaults.fyi API and signed by Para
- Track on-chain positions across every protocol the user holds vault shares in, including positions opened outside this app
- Claim accrued rewards across protocols via the two-step rewards flow

## How this relates to Paxos Amplify

Para's existing [Stablecoin Yield via Paxos Amplify walkthrough](https://docs.getpara.com/v2/walkthroughs/amplify) gives you three Paxos-curated strategies (CORE, TREASURY, FRONTIER). vaults.fyi extends that to 1,000+ vaults across 82 protocols, with curators including Gauntlet, Steakhouse, Chaos Labs, Sentora, and kpk. Use both, or use vaults.fyi when you need coverage beyond Paxos. The two coexist cleanly: both deposit calldata flows target canonical vault contracts directly, so positions opened with one show up in the other's portfolio view.

## Getting Started

### Environment Setup

1. Clone the repository
2. Copy the `.env.example` file to `.env.local`:

```bash
cp .env.example .env.local
```

3. Configure the required environment variables:

```
NEXT_PUBLIC_PARA_API_KEY=your-para-api-key
NEXT_PUBLIC_PARA_ENVIRONMENT=BETA
NEXT_PUBLIC_BASE_RPC_URL=https://mainnet.base.org
VAULTS_FYI_API_KEY=your-vaultsfyi-api-key
```

- For a Para API key, visit [developer.getpara.com](https://developer.getpara.com). Use the public API Key (the Secret API Key is for backend services only and is not used here).
- For a vaults.fyi API key, sign up at [portal.vaults.fyi](https://portal.vaults.fyi). Keys are typically issued within one business day.
- You can use the public Base mainnet RPC for development or get a dedicated endpoint from providers like Alchemy, QuickNode, or Infura.

`VAULTS_FYI_API_KEY` is **not** prefixed with `NEXT_PUBLIC_`. It is read server-side by the `/api/vaultsfyi/[...path]` proxy route and injected as the `x-api-key` header. The key never reaches the browser bundle.

### Installation

Install dependencies:

```bash
yarn install
```

### Running the App

Start the development server:

```bash
yarn dev
```

Open [http://localhost:3000](http://localhost:3000) in your browser to see the application.

## How It Works

1. The app uses Para SDK to handle wallet connections and transaction signing.
2. Vault data is fetched from the vaults.fyi API through a Next.js route handler that proxies requests with the server-side API key.
3. When a user selects a vault and clicks deposit:
- The app calls `/v2/transactions/deposit/{user}/base/{vaultId}` to get an ordered `actions` array of unsigned transactions.
- For each step, Para signs the transaction via a viem `WalletClient` built from `createParaViemAccount` and `createParaViemClient`.
- The signed transaction is broadcast to Base.
- The app waits for the receipt before submitting the next step.
4. Position tracking and reward claiming follow the same `actions[]` pattern.

## Endpoints used

| Step | Endpoint | What you get |
|---|---|---|
| Discover | `GET /v2/detailed-vaults` (filtered by USDC + Base) | Top USDC vaults on Base ranked by 7-day APY |
| Deposit | `GET /v2/transactions/deposit/:user/:network/:vaultId` | An ordered `actions[]` array, each with `tx.to`, `tx.data`, `tx.chainId`, `tx.value` ready to sign |
| Track | `GET /v2/portfolio/positions/:userAddress` | Every vault position the user holds across every protocol |
| Redeem | `GET /v2/transactions/redeem/:user/:network/:vaultId?all=true` | Same shape as deposit |
| Claim | `GET /v2/transactions/rewards/context/:userAddress` then `/rewards/claim` | Two-step. Discover claim ids, then per-network transactions |

## Note about real funds

vaults.fyi indexes mainnet vaults only. There is no testnet equivalent for the Morpho, Aave, Sky, or Spark vaults you'd deposit into. The deposit flow in this recipe defaults to 1 USDC, which is enough to runtime-test the integration end-to-end for ~$1 of real funds. Use a fresh Para wallet for testing if you'd prefer to keep this isolated from your main account.

## Learn More

- Para SDK Documentation: [docs.getpara.com](https://docs.getpara.com)
- Para Paxos Amplify walkthrough (sibling integration): [docs.getpara.com/v2/walkthroughs/amplify](https://docs.getpara.com/v2/walkthroughs/amplify)
- vaults.fyi Documentation: [docs.vaults.fyi](https://docs.vaults.fyi)
- vaults.fyi OpenAPI spec: [api.vaults.fyi/v2/documentation](https://api.vaults.fyi/v2/documentation/static/index.html)
- @vaultsfyi/sdk reference: [docs.vaults.fyi/sdk/reference](https://docs.vaults.fyi/sdk/reference)
8 changes: 8 additions & 0 deletions defi-integrations/with-vaultsfyi-api/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import js from "@eslint/js";
import tseslint from "typescript-eslint";

export default tseslint.config(
{ ignores: [".next", "node_modules"] },
js.configs.recommended,
...tseslint.configs.recommended,
);
50 changes: 50 additions & 0 deletions defi-integrations/with-vaultsfyi-api/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { NextConfig } from "next";

/**
* Para SDK 2.27.0 ships optional integrations (Stellar, MetaMask Delegation
* Toolkit, etc.) whose peer dependencies are NOT installed by any of the
* `defi-integrations/` peers in examples-hub. Without resolving them to empty
* modules, `yarn dev` and `yarn build` both fail with module-not-found on
* first compile — confirmed against Jupiter / Relay / Squid peers as of
* 2026-05-25.
*
* The vaults.fyi recipe is EVM-only (viem + @getpara/viem-v2-integration), so
* we never touch these integrations at runtime. The fallback config below
* tells webpack to substitute empty modules and let the Para SDK's runtime
* checks handle them as "integration unavailable."
*
* If Para upstreams a fix for tree-shaking these optional integrations, this
* block becomes unnecessary.
*/
const PARA_SDK_OPTIONAL_DEPS = [
"@stellar/stellar-sdk",
"@metamask/delegation-toolkit",
"ethers", // Para SDK ships ethers v5/v6 EVM signers alongside viem; this recipe uses viem only.
"@getpara/ethers-v6-integration",
"@farcaster/miniapp-wagmi-connector",
"@farcaster/mini-app-solana",
"@farcaster/miniapp-sdk",
];

const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "**",
port: "",
pathname: "**",
},
],
},
webpack: (config) => {
config.resolve = config.resolve ?? {};
config.resolve.fallback = {
...(config.resolve.fallback ?? {}),
...Object.fromEntries(PARA_SDK_OPTIONAL_DEPS.map((p) => [p, false])),
};
return config;
},
};

export default nextConfig;
50 changes: 50 additions & 0 deletions defi-integrations/with-vaultsfyi-api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "with-vaultsfyi",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@cosmjs/cosmwasm-stargate": "^0.34.0",
"@cosmjs/launchpad": "^0.27.1",
"@cosmjs/proto-signing": "^0.34.0",
"@cosmjs/stargate": "^0.34.0",
"@cosmjs/tendermint-rpc": "^0.34.0",
"@getpara/react-sdk": "2.27.0",
"@getpara/solana-web3.js-v1-integration": "2.27.0",
"@getpara/viem-v2-integration": "2.27.0",
"@solana-mobile/wallet-adapter-mobile": "^2.2.2",
"@solana/wallet-adapter-base": "^0.9.27",
"@solana/wallet-adapter-react": "^0.15.39",
"@solana/wallet-adapter-walletconnect": "^0.1.21",
"@solana/web3.js": "1.98.2",
"@tanstack/react-query": "^5.81.2",
"@vaultsfyi/sdk": "^2.3.5",
"graz": "^0.4.1",
"long": "^5.3.2",
"lucide-react": "0.483.0",
"next": "15.1.5",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"viem": "^2.33.0"
},
"devDependencies": {
"@eslint/js": "^9.29.0",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"autoprefixer": "^10.4.20",
"eslint": "^9.29.0",
"pino-pretty": "13.0.0",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5",
"typescript-eslint": "^8.22.0"
}
}
9 changes: 9 additions & 0 deletions defi-integrations/with-vaultsfyi-api/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

export default config;
3 changes: 3 additions & 0 deletions defi-integrations/with-vaultsfyi-api/public/para.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { NextRequest, NextResponse } from "next/server";

const API_BASE = "https://api.vaults.fyi";

/**
* Catch-all proxy that forwards every /api/vaultsfyi/* request to api.vaults.fyi
* with the server-side x-api-key header attached. Keeps VAULTS_FYI_API_KEY out
* of the browser bundle.
*
* The @vaultsfyi/sdk client in src/lib/vaultsFyi.ts is configured with
* apiBaseUrl = `${window.location.origin}/api/vaultsfyi`, so SDK calls land
* here first and the upstream service sees them with the correct auth header.
*
* For production deploys: this route works as-is on Vercel / Next.js hosts.
* If you self-host or split frontend/backend, replicate the header injection
* in your own gateway.
*/
async function proxy(
request: NextRequest,
params: Promise<{ path: string[] }>,
) {
const apiKey = process.env.VAULTS_FYI_API_KEY;
if (!apiKey) {
return NextResponse.json(
{
error:
"VAULTS_FYI_API_KEY is not set on the server. Add it to .env.local and restart.",
},
{ status: 500 },
);
}

const { path } = await params;
const url = new URL(`${API_BASE}/${path.join("/")}`);
request.nextUrl.searchParams.forEach((value, key) => {
url.searchParams.append(key, value);
});

const upstream = await fetch(url, {
method: request.method,
headers: {
"x-api-key": apiKey,
Accept: "application/json",
},
});

const body = await upstream.text();
return new NextResponse(body, {
status: upstream.status,
headers: {
"Content-Type":
upstream.headers.get("Content-Type") ?? "application/json",
},
});
}

export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
return proxy(request, params);
}
14 changes: 14 additions & 0 deletions defi-integrations/with-vaultsfyi-api/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--background: #ffffff;
--foreground: #171717;
}

body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}
34 changes: 34 additions & 0 deletions defi-integrations/with-vaultsfyi-api/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { ParaProvider } from "@/context/ParaProvider";
import { QueryProvider } from "@/context/QueryProvider";
import Header from "@/components/Header";
import "./globals.css";
import "@getpara/react-sdk/styles.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
title: "vaults.fyi Yield Integration",
description:
"Deposit, track, and claim DeFi yield across 1,000+ vaults using vaults.fyi and Para embedded wallets.",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" className={inter.className}>
<body>
<QueryProvider>
<ParaProvider>
<Header />
<main>{children}</main>
</ParaProvider>
</QueryProvider>
</body>
</html>
);
}
Loading