# Rewards Widget

***

### Integrating the Rewards Widget

For the first time ever, stakers can [sell their future rewards](/stakers.md#what-stakers-can-do-on-pye) for SOL via Pye. The Rewards Widget lets active stakers delegated to your validator lockup stake for different time durations and sell their future rewards directly from your site. This guide covers the different integration paths available.

### Prerequisites

You'll need four values before you start:

<table><thead><tr><th>Value</th><th width="368">What it is</th></tr></thead><tbody><tr><td><strong>Vote Account</strong></td><td>Your validator's <strong>vote account address</strong></td></tr><tr><td><strong>RPC endpoint</strong></td><td><a href="https://api.mainnet-beta.solana.com  ">https://api.mainnet-beta.solana.com  </a></td></tr><tr><td><strong>Supabase URL</strong></td><td><a href="https://tfrickmnrfyjkvjhmuik.supabase.co">https://tfrickmnrfyjkvjhmuik.supabase.co</a></td></tr><tr><td><strong>Supabase Anon Key</strong></td><td><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InRmcmlja21ucmZ5amt2amhtdWlrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDM0NDkyMDksImV4cCI6MjA1OTAyNTIwOX0.1wl2FWa5g0tkUn6yRcg1AyF6ixWN7SyoD89cFGxkQKM</code></td></tr></tbody></table>

#### Finding your vote account address

Your vote account is the on-chain address that receives staking rewards. It's not your identity account or withdraw authority. Using the wrong one will cause the widget to show no stake positions.

Where you can find it:

* [**stakewhiz**](https://www.validators.app/ahttps://stakewiz.com/) — search your validator name; the vote account appears on your profile page.
* [**Solana Explorer**](https://explorer.solana.com/validators) — search by validator name or identity key.
* **Solana CLI** — `solana validators | grep <your-name>`

### Configuration

You can configure what mode the widget runs in as well as its style theme.&#x20;

Single-validator mode filters stake positions to only those delegated to your vote account. Universal mode shows positions across all supported validators, enable it by omitting the vote account prop.

| Mode                 | What it shows                                              | How to configure   |
| -------------------- | ---------------------------------------------------------- | ------------------ |
| **Single-validator** | Only stake accounts delegated to your validator            | Pass `voteAccount` |
| **Universal**        | All Pye-compatible stake accounts for the connected wallet | Omit `voteAccount` |

The widget style defaults to `pye-dark`. Pass one of the following `theme` props (React and Next.js) or set the `data-theme` attribute (CDN).

| Value       | Appearance                           |
| ----------- | ------------------------------------ |
| `pye-light` | Light background, purple accent      |
| `pye-dark`  | Dark background, purple accent       |
| `graphite`  | Dark background, neutral grey accent |

### Installation

{% tabs %}
{% tab title="React (Vite)" %}

#### 1. Install packages

bash

```bash
npm install --legacy-peer-deps @pyefi/sdk @pyefi/widget \
  react react-dom \
  @solana/web3.js @solana/spl-token \
  @solana/wallet-adapter-react @solana/wallet-adapter-base @solana/wallet-adapter-wallets \
  @solana/kit@^2 @cks-systems/manifest-sdk \
  @supabase/supabase-js \
  buffer stream-browserify assert
```

Or with pnpm:

bash

```bash
pnpm add @pyefi/sdk @pyefi/widget \
  react react-dom \
  @solana/web3.js @solana/spl-token \
  @solana/wallet-adapter-react @solana/wallet-adapter-base @solana/wallet-adapter-wallets \
  @solana/kit@^2 @cks-systems/manifest-sdk \
  @supabase/supabase-js \
  buffer stream-browserify assert
```

> **Note** — `buffer`, `stream-browserify`, and `assert` are runtime polyfills required by Solana packages in a browser Vite build.

#### 2. Configure Vite

Update `vite.config.ts` to polyfill the Node.js globals that Solana packages depend on:

typescript

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  define: {
    "process.env": {},
    "process.version": JSON.stringify("v18.0.0"),
    "process.browser": true,
  },
  resolve: {
    alias: {
      stream: "stream-browserify",
      assert: "assert",
    },
  },
});
```

#### 3. Configure TypeScript

If you don't already have a `tsconfig.json`, create one. Make sure it includes `"vite/client"` in `types` — this gives TypeScript the `import.meta.env` definitions that Vite injects at build time:

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "skipLibCheck": true,
    "types": ["vite/client"]
  },
  "include": ["src"]
}
```

If you already have a `tsconfig.json`, add `"types": ["vite/client"]` to `compilerOptions`.

#### 4. Add the Buffer polyfill

> **Warning** — These two lines must be the **very first lines** of your app entry file (e.g. `src/main.tsx`), before any other imports. If they run after Solana packages load, the polyfill won't take effect.

```typescript
import { Buffer } from "buffer";
(window as any).Buffer = Buffer;

// ...rest of your imports
```

#### 5. Set environment variables

Create `.env.local`:

bash

```bash
VITE_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
VITE_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
VITE_SUPABASE_ANON_KEY=YOUR_ANON_KEY
VITE_VOTE_ACCOUNT=YOUR_VOTE_ACCOUNT
```

#### 6. Render the widget

```tsx
import { useState } from "react";
import { PyeWidget } from "@pyefi/widget";

export default function App() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Get paid now</button>

      {open && (
        <div
          style={{
            position: "fixed", inset: 0,
            display: "flex", alignItems: "center", justifyContent: "center",
            background: "rgba(0,0,0,0.5)", backdropFilter: "blur(8px)",
            zIndex: 9999,
          }}
          onClick={(e) => { if (e.target === e.currentTarget) setOpen(false); }}
        >
          <PyeWidget
            rpcUrl={import.meta.env.VITE_RPC_URL}
            supabaseUrl={import.meta.env.VITE_SUPABASE_URL}
            supabaseAnonKey={import.meta.env.VITE_SUPABASE_ANON_KEY}
            voteAccount={import.meta.env.VITE_VOTE_ACCOUNT}
            theme="pye-dark"
            onClose={() => setOpen(false)}
          />
        </div>
      )}
    </>
  );
}
```

{% endtab %}

{% tab title="Next.js (App Router)" %}

#### 1. Install packages

```bash
npm install --legacy-peer-deps @pyefi/sdk @pyefi/widget \
  react react-dom \
  @solana/web3.js @solana/spl-token \
  @solana/wallet-adapter-react @solana/wallet-adapter-base @solana/wallet-adapter-wallets \
  "@solana/kit@^2" @cks-systems/manifest-sdk \
  @supabase/supabase-js \
  next
```

#### 2. Configure Next.js

Update `next.config.ts`:

```ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  transpilePackages: ["@pyefi/sdk", "@pyefi/widget"],
  turbopack: {},
};

export default nextConfig;
```

`transpilePackages` is required because `@pyefi/widget` ships as an ES module. `turbopack: {}` is required for Next.js 16+, which uses Turbopack by default — it automatically handles Node.js built-in exclusions for browser bundles so no additional webpack fallback config is needed.

> **Next.js 15 or earlier:** Replace `turbopack: {}` with a webpack config instead:
>
> ```ts
> webpack: (config) => {
>   config.resolve.fallback = { ...config.resolve.fallback, fs: false, net: false, tls: false };
>   return config;
> },
> ```

#### 3. Set environment variables

Create `.env.local`:

```
NEXT_PUBLIC_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
NEXT_PUBLIC_SUPABASE_URL=https://tfrickmnrfyjkvjhmuik.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InRmcmlja21ucmZ5amt2amhtdWlrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDM0NDkyMDksImV4cCI6MjA1OTAyNTIwOX0.1wl2FWa5g0tkUn6yRcg1AyF6ixWN7SyoD89cFGxkQKM
NEXT_PUBLIC_VOTE_ACCOUNT=YOUR_VOTE_ACCOUNT
```

#### 4. Render the widget

The component must be in a Client Component (add `"use client"` at the top):

```tsx
"use client";

import { useState } from "react";
import { PyeWidget } from "@pyefi/widget";

export default function Page() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Get paid now</button>

      {open && (
        <div
          style={{
            position: "fixed", inset: 0,
            display: "flex", alignItems: "center", justifyContent: "center",
            background: "rgba(0,0,0,0.5)", backdropFilter: "blur(8px)",
            zIndex: 9999,
          }}
          onClick={(e) => { if (e.target === e.currentTarget) setOpen(false); }}
        >
          <PyeWidget
            rpcUrl={process.env.NEXT_PUBLIC_RPC_URL!}
            supabaseUrl={process.env.NEXT_PUBLIC_SUPABASE_URL!}
            supabaseAnonKey={process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!}
            voteAccount={process.env.NEXT_PUBLIC_VOTE_ACCOUNT!}
            theme="pye-dark"
            onClose={() => setOpen(false)}
          />
        </div>
      )}
    </>
  );
}
```

> **Universal mode** — Omit the `voteAccount` prop and the `NEXT_PUBLIC_VOTE_ACCOUNT` env variable.
> {% endtab %}

{% tab title="HTML/CDN" %}

### HTML / CDN

No build step required. Add a single `<script>` tag to any HTML page.

#### Single-validator

html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My Validator</title>
</head>
<body>
  <div id="pye-widget"></div>

  <script
    data-pye-widget
    data-vote-account="YOUR_VOTE_ACCOUNT"
    data-theme="pye-dark"
    data-rpc-url="https://mainnet.helius-rpc.com/?api-key=YOUR_KEY"
    data-supabase-url="https://YOUR_PROJECT.supabase.co"
    data-supabase-anon-key="YOUR_ANON_KEY"
    src="REPLACE_WITH_CDN_URL"
  ></script>
</body>
</html>
```

#### Universal

Omit `data-vote-account`:

html

```html
<script
  data-pye-widget
  data-theme="pye-dark"
  data-rpc-url="https://mainnet.helius-rpc.com/?api-key=YOUR_KEY"
  data-supabase-url="https://YOUR_PROJECT.supabase.co"
  data-supabase-anon-key="YOUR_ANON_KEY"
  src="REPLACE_WITH_CDN_URL"
></script>
```

#### How it mounts

The script looks for a `<div id="pye-widget">` element and mounts into it. If none exists, it creates one and appends it to `<body>`. All configuration is read from the `data-*` attributes on the `<script>` tag itself.

***

### React / Next.js props

| Prop              | Type                                      | Required | Default       | Description                            |
| ----------------- | ----------------------------------------- | -------- | ------------- | -------------------------------------- |
| `rpcUrl`          | `string`                                  | yes      | —             | Solana mainnet RPC endpoint            |
| `supabaseUrl`     | `string`                                  | yes      | —             | Supabase project URL                   |
| `supabaseAnonKey` | `string`                                  | yes      | —             | Supabase anonymous key                 |
| `voteAccount`     | `string`                                  | no       | —             | Vote account for single-validator mode |
| `theme`           | `"pye-light" \| "pye-dark" \| "graphite"` | no       | `"pye-light"` | Visual theme                           |
| `onClose`         | `() => void`                              | no       | —             | Called when the user closes the widget |

### CDN data attributes

| Attribute                | Required | Default                               | Description                                        |
| ------------------------ | -------- | ------------------------------------- | -------------------------------------------------- |
| `data-pye-widget`        | yes      | —                                     | Identifies the script tag (no value needed)        |
| `data-supabase-url`      | yes      | —                                     | Supabase project URL                               |
| `data-supabase-anon-key` | yes      | —                                     | Supabase anonymous key                             |
| `data-rpc-url`           | no       | `https://api.mainnet-beta.solana.com` | Solana RPC endpoint                                |
| `data-vote-account`      | no       | —                                     | Vote account for single-validator mode             |
| `data-theme`             | no       | `pye-light`                           | Visual theme (`pye-light`, `pye-dark`, `graphite`) |
| {% endtab %}             |          |                                       |                                                    |
| {% endtabs %}            |          |                                       |                                                    |

### RPC endpoint

{% hint style="info" %}
**Important** — The public endpoint (`https://api.mainnet-beta.solana.com`) works for development but has rate limits that will cause wallet and balance fetches to fail under normal production usage. Use a dedicated provider in production.
{% endhint %}

Recommended providers:

* [**Helius**](https://helius.dev)&#x20;
* [**Triton**](https://triton.one)
* [**QuickNode**](https://quicknode.com)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.pye.fi/validators/rewards-widget.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
