# Rewards Widget

***

### Integrating the Rewards Widget

The Rewards Widget lets stakers delegated to your validator sell their future rewards for SOL, directly from your site. You keep the delegation. They get paid up front. This guide covers the three supported integration paths: React, Next.js, and a plain HTML/CDN drop-in.

### 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>An <strong>RPC endpoint</strong></td><td>The public Solana endpoint works for development but recommend a dedicated endpoint for production</td></tr><tr><td><strong>Supabase URL</strong></td><td>Request access</td></tr><tr><td><strong>Supabase Anon Key</strong></td><td>Request access</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

Two things to configure before you drop the widget in: which mode it runs in, and which theme it uses.

**Mode.** Single-validator mode filters stake positions to only those delegated to your vote account. That's what you want on your own site. Universal mode shows positions across all supported validators. Use it by omitting the vote account.

| 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` |

**Theme.** The widget ships with `pye-dark`. Pass it as the `theme` prop (React and Next.js) or 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 @pye/sdk @pye/widget \
  react react-dom \
  @solana/web3.js @solana/spl-token \
  @solana/wallet-adapter-react @solana/wallet-adapter-react-ui \
  @solana/kit @cks-systems/manifest-sdk \
  @supabase/supabase-js \
  buffer stream-browserify assert
```

Or with pnpm:

bash

```bash
pnpm add @pye/sdk @pye/widget \
  react react-dom \
  @solana/web3.js @solana/spl-token \
  @solana/wallet-adapter-react @solana/wallet-adapter-react-ui \
  @solana/kit @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. 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
```

#### 4. 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
```

#### 5. Render the widget

```tsx
import { useState } from "react";
import { PyeWidget } from "@pye/widget";
import type { WidgetTheme } from "@pye/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>
      )}
    </>
  );
}
```

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

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

#### 1. Install packages

bash

```bash
npm install @pye/sdk @pye/widget \
  react react-dom \
  @solana/web3.js @solana/spl-token \
  @solana/wallet-adapter-react @solana/wallet-adapter-react-ui \
  @solana/kit @cks-systems/manifest-sdk \
  @supabase/supabase-js \
  next
```

#### 2. Configure Next.js

Update `next.config.ts`:

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

const nextConfig: NextConfig = {
  transpilePackages: ["@pye/sdk", "@pye/widget"],
  webpack: (config) => {
    config.resolve.fallback = {
      ...config.resolve.fallback,
      fs: false,
      net: false,
      tls: false,
    };
    return config;
  },
};

export default nextConfig;
```

> **Note** — Both `transpilePackages` and `webpack.resolve.fallback` are required. The fallbacks prevent build errors from Solana packages that reference Node.js built-ins.

#### 3. Set environment variables

Create `.env.local`:

bash

```bash
NEXT_PUBLIC_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
NEXT_PUBLIC_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_ANON_KEY
NEXT_PUBLIC_VOTE_ACCOUNT=YOUR_VOTE_ACCOUNT
```

#### 4. Render the widget

> **Important** — The widget must be rendered inside a **Client Component**. Add `"use client"` at the top of the file.

```tsx
"use client";

import { useState } from "react";
import { PyeWidget } from "@pye/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) — `https://mainnet.helius-rpc.com/?api-key=YOUR_KEY`
* [**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.
