# SHORTCUTS

***

<table data-view="cards"><thead><tr><th></th></tr></thead><tbody><tr><td>👉 Where to Buy?</td></tr><tr><td>🟣 Thor Pass (Classic/Elite/Gold)</td></tr><tr><td>🟠 Flash Pass</td></tr><tr><td>⚫️ Prime Pass</td></tr><tr><td>🚀 Rent Dedicated Node Servers</td></tr><tr><td>✍️ <a href="/pages/wmnTbIZ82YIa6RtdKPrz">Manage Your Authentication</a></td></tr><tr><td>🧵<a href="/pages/z2SVLlMfuQp9VpZZAzfE">How to Rent a Node?</a></td></tr><tr><td>🖥️ <a href="https://cloud.thornode.io/">Buy VPS</a></td></tr><tr><td>🆘 Troubleshooting</td></tr><tr><td>🚀 Solana gRPC</td></tr><tr><td>🧿 <a href="/pages/OByrRDhpQ8GGJtNhcLNc">MEV Protection</a></td></tr><tr><td>⏩ Fast Transaction Landing</td></tr></tbody></table>

***

<table data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td>Earn revenue share in your Discord server</td><td></td></tr><tr><td><p>Raw Solana shreds</p><p>- the fastest on-chain signal</p></td><td><a href="/pages/J4Mv6wiJvBD7pBO409zl">/pages/J4Mv6wiJvBD7pBO409zl</a></td></tr></tbody></table>

***

<table data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td>Local load balancing tool</td><td></td></tr><tr><td>Yellowstone gRPC tool</td><td></td></tr></tbody></table>


# Welcome

Welcome to the Thor Labs Node Pass authentication documentation. This guide will help you set up and manage your RPC node access through our Discord bot system.

## What is Thor Labs Node Pass?

Thor Labs Node Pass is an NFT-based authentication system that provides access to premium RPC nodes across multiple blockchains. By holding a Node Pass, you can whitelist your IP address to access high-performance, dedicated RPC endpoints.

## Pass Types Overview

| Pass Type    |        Access Level |                      Double IP | Special Features                                             |
| ------------ | ------------------: | -----------------------------: | ------------------------------------------------------------ |
| **Classic**  |            Standard |                             No | Basic multi-location access                                  |
| **Elite**    |            Enhanced |                            Yes | Double IP capability and Elite-only locations                |
| **Gold**     |            Enhanced |                            Yes | Same as Elite                                                |
| **Flash**    |             Limited |                             No | Flash-exclusive locations only                               |
| **Honorary** |             Special |                             No | Single IP limit                                              |
| **Prime**    | Premium (Dedicated) | 4 shared + unlimited dedicated | 4 shared locations (1 IP each) + dedicated RPC, 24/7 support |


# Where Do I Buy a ThorNode Pass?

##

You can buy a pass in Magic Eden. If you want to buy a Prime Pass, please contact with the team before proceeding.\
[**https://magiceden.io/creators/thorlabs**](https://magiceden.io/creators/thorlabs)

{% hint style="danger" %}
**OTC or ZERO-ROYALTY TRADE IS FORBIDDEN!**
{% endhint %}

Our low renewal price regime also depends on royalties. If you want to be a holder, you should prefer trading by royalty. Thus, the ThorNode board decided to BAN OTC trading. If you do OTC trade, you have to send related royalty share to thornode.sol address. Otherwise your NFT will be burned and reissued.\
\
If you do not pay the royalty during trade, you need to pay enforced royalty fee as given below:\
**For Classic Pass:**\
Floor price is below 5 SOL- Fee is 0.5 SOL\
Floor price is above 5 SOL- Fee is floor/10 in SOL\
\
**For Elite Pass:**\
Floor price is below 10 SOL - Fee is 1 SOL\
Floor price is above 10 SOL- Fee is floor/10 in SOL

**For Prime Pass:**\
Floor price is below 70 SOL - Fee is 7 SOL\
Floor price is above 70 SOL- Fee is floor/10 in SOL


# Quick Start

{% stepper %}
{% step %}
**Verify your wallet**

Connect your wallet holding the Node Pass in the Thor Labs Discord verification channel.\
<https://verify-beta.thornode.io/>
{% endstep %}

{% step %}
**Open a DM**

Send a direct message to the Auth Bot.
{% endstep %}

{% step %}
**Run the command**

Type the command below in the DM to begin authentication:

```
!rpc auth
```

{% endstep %}

{% step %}
**Follow the prompts**

Select your pass, choose a location, and enter your IP address when prompted by the bot.
{% endstep %}
{% endstepper %}

## Documentation Sections

* [Thor Pass Authentication](broken://pages/wmnTbIZ82YIa6RtdKPrz) - Guide for Classic, Elite, and Gold pass holders
* Flash Pass Authentication - Guide for Flash pass holders
* Prime Pass Authentication - Guide for Prime pass holders
* [Rental System Guide](broken://pages/z2SVLlMfuQp9VpZZAzfE) - How to rent and manage rented slots
* [Managing Your Authentication](broken://pages/wmnTbIZ82YIa6RtdKPrz) - Update, delete, and manage your IPs
* Troubleshooting & FAQ - Common issues and solutions

## Available Commands

All commands are run in **Direct Messages** with the Auth Bot:

| Command       | Description                              |
| ------------- | ---------------------------------------- |
| `!rpc auth`   | Authenticate a new IP address            |
| `!rpc delete` | View and manage existing authentications |
| `!rpc remark` | Add notes to your authentications        |
| `!rpc rents`  | View your active rentals (pass holders)  |
| `!rpc token`  | View your gRPC tokens                    |
| `!rpc help`   | Display help information                 |

{% hint style="info" %}
Need Help?

* Check the Troubleshooting page
* Contact Thor Labs support in the Discord server
* Report bugs with the error code displayed (e.g., Dx70, Rx306)
  {% endhint %}


# Prime Pass and Dedicated Nodes

## What is ThorNode Prime Pass?

ThorNode Prime Pass is the ultimate tier offering **managed dedicated Solana RPCs** built specifically for each customer. Prime Pass holders get access to both shared RPC locations AND their own dedicated standalone RPC infrastructure.

{% hint style="danger" %}
**Why Prime Pass?**

**ThorNode Prime Pass offers the most competitive pricing for dedicated Solana RPC nodes in the market - starting at just $1000. You won't find a fully managed, dedicated Solana RPC at this price point anywhere else.**<br>

**It's unmatched value for individuals, DAOs, and projects who need reliable, dedicated infrastructure.**
{% endhint %}

{% hint style="success" %}
**You can rent a dedicated RPC from ThorLas without holding a Prime Pass, but without general Prime Pass perks. Contact support for more info and pricing.**
{% endhint %}

## Prime Pass Overview

| Feature          | Details                                                                 |
| ---------------- | ----------------------------------------------------------------------- |
| Pass ID Format   | `P{number}` or `T{number}` (e.g., P123, T456)                           |
| Total Supply     | **10 NFTs** (limited for quality of service)                            |
| Shared Locations | **4 locations** (1 IP each, same locations as Classic/Elite/Flash)      |
| Dedicated RPC    | **Standalone RPC** with unlimited IPs and RPS/TPS                       |
| gRPC             | <p>ThorStreamer included<br>Yellowstone, on demand with extra cost.</p> |
| Setup Time       | 2-3 business days                                                       |
| Support          | **24/7 support plan**                                                   |
| Shared SWQoS     | Yes                                                                     |
| Dedicated SWQoS  | On demand with extra cost                                               |
| Bifrost          | 50 TPS                                                                  |

## Who is Prime Pass For?

ThorNode Prime is perfect for:

* **Individuals** who want full control and fastest possible over their RPC infrastructure
* **DAOs** requiring reliable, dedicated access
* **Projects** needing high-performance Solana RPC for production use

## Two-Part Access System

Prime Pass provides two types of RPC access:

### Shared Location Access (4 Locations)

You select **4 locations** from all available ThorLabs shared locations (same pool as Classic/Elite/Flash passes):

* **1 IP per location** - authenticate one IP address at each of your 4 chosen locations
* **Unique advantage** - You can select the same location multiple times (e.g., 4x Virginia = 4 different IPs on the same endpoint)
* Other pass types cannot authenticate multiple IPs to the same endpoint like this

Example configurations:

* 4 different locations (Virginia, Frankfurt, Tokyo, London)
* 2x Virginia + 2x Frankfurt
* 4x Virginia (4 different IPs on one endpoint)

### Dedicated Standalone RPC

In addition to shared locations, you receive your own **dedicated RPC**:

* Built specifically for you within 2-3 business days
* **Unlimited IP authentications** - add as many IPs as you need
* Not shared with any other users
* Full control over your dedicated infrastructure
* Perfect for teams and scaling

#### Next Steps

* Manage your Prime Pass URLs and IP authentication
* Pricing
* [RPC Node Locations and URLs](/introduction/node-locations-and-access)
* Troubleshooting - Solutions for common issues


# ThorNode Passes

ThorNode is a powerful private Solana RPC service. Earn staking rewards while having access to strong private RPC nodes.

## What is ThorNode Pass?

ThorNode has three type of NFTs,

### Classic Pass

### Information:

* Available Locations: Germany, Netherlands, Virginia, New York, Salt Lake City, Tokyo
* IP per Pass: 4
* Renewal Fee: $350 per Month
* Rate Limit: Up to 600 Request per Second
* Request Limit: Unlimited

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-ee4640187b8eece1c977f751ae1f539709ee5b21%2Fimage.png?alt=media" alt="" width="320"><figcaption></figcaption></figure>

### Elite Pass

### Information:

* Available Locations: Germany, Netherlands, Virginia, New York, Salt Lake City, Tokyo
* Bonus: Another RPC node in Frankfurt dedicated to Elite Pass users only.
* IP per Pass: 4 + 1
* Renewal Fee: $350 per Month
* Rate Limit: Up to 600 Request per Second
* Request Limit: Unlimited
* Elite Pass holders can authorize different IP for each location plus they can authorize 2 IP for one selected location.

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-bfc56d2afd74e7353ca615a91bf4e6277bcf629b%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Elite Pass holders can authorize two different IPs at the same time while Classic Pass can authorize one IP.
{% endhint %}

### Prime Pass

ThorNode Prime Pass offers customers to have their managed dedicated Solana RPCs. Prime Pass RPCs are built by the request of the customers and delivered within 2-3 business days. Currently there is only 10 ThorNode Prime Pass NFTs available for a better quality of service. ThorNode Prime is perfect for those who want to be in control of their own destiny, for individuals, DAOs or projects. With ThorNode Prime, you also have 24/7 support plan.

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-667350acb274e24a07ac70057066f8dd70fc238c%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

### Gold Pass

Gold Pass allows connecting to THOR's RPCs free for life time. Gold Pass holders can authorize different IP for each location plus they can authorize 2 IP for one selected location.

<br>

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-e3198e7467eae692247998d9a4d2c40cc6c36b5b%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

## Honorary Pass

The Honorary Pass is a prestigious offering designed to recognize and celebrate influential figures in the Solana community who have significantly contributed to its growth and success. This exclusive pass is more than just a symbol of honor; it's a testament to the impactful legacy these community leaders have built.

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-d20b47f88689ddab50e55a3c61a81c7d2ad3925b%2Fimage.png?alt=media" alt="" width="375"><figcaption></figcaption></figure>

## Pass Comparison

| Feature                | Classic   | Elite     | Gold      |
| ---------------------- | --------- | --------- | --------- |
| Single IP per location | Yes       | Yes       | Yes       |
| Double IP per location | No        | Yes       | Yes       |
| Elite-only locations   | No        | Yes       | Yes       |
| Max authentications    | Unlimited | Unlimited | Unlimited |

## Next Steps

* [Managing Your Authentication](/introduction/node-locations-and-access/authentication) - Learn to update and delete your IP
* [RPC Node Locations and URLs](/introduction/node-locations-and-access)
* [Renewal Guide](/introduction/renewals-and-subscriptions)
* [Rental System Guide](broken://pages/z2SVLlMfuQp9VpZZAzfE) - Rent additional locations from other users
* Troubleshooting - Solutions for common issues


# Flash Node Passes

This guide covers the authentication process specifically for **Flash Pass** holders.

## What is Flash Pass?

Flash Pass is a limited-access tier that provides entry to exclusive Flash-only RPC node locations. It's designed for users who need access to specific high-demand locations at a lower entry point.

## Flash Pass Characteristics

| Feature             | Flash Pass                       |
| ------------------- | -------------------------------- |
| Renewal Fee         | $100                             |
| Rate Limit          | Up to 400 Request per Second     |
| Request Limit       | Unlimited                        |
| Pass ID Format      | `F{number}` (e.g., F123)         |
| Double IP           | Not available                    |
| Max authentications | **1 IP total**                   |
| Location access     | Flash-only locations exclusively |

## Flash-Only Locations

Flash Pass holders can **only** authenticate to these locations:

| Location       | Blockchain          |
| -------------- | ------------------- |
| Los Angeles    | Solana              |
| London         | Solana              |
| Salt Lake City | Solana              |
| Frankfurt      | Binance Smart Chain |

> **Important:** Flash Pass cannot access any other locations. Attempting to select non-Flash locations will result in an error.

## Single IP Limitation

Unlike other pass types, Flash Pass has a strict **one IP per pass** limitation:

* You can only authenticate **one IP address** total with your Flash Pass
* This IP can be at any one Flash-only location
* To change locations, you must first delete your existing authentication

## Step-by-Step Authentication

{% stepper %}
{% step %}
**Start the Process**

Open a **Direct Message** with the Auth Bot and type:

```
!rpc auth
```

{% endstep %}

{% step %}
**Select Your Flash Pass**

From the dropdown menu, select your Flash Pass (shown as `F{number}`).

> **Subscription Check:** If your Flash Pass subscription has expired, you'll need to renew before proceeding.
> {% endstep %}

{% step %}
**Choose a Flash Location**

Select from the available Flash-only locations:

```
Los Angeles 🇺🇸
London 🇬🇧
Salt Lake City (Flash) 🇺🇸
Frankfurt BSC 🇩🇪
```

If you select a non-Flash location you'll receive an error message:

"You can only authenticate to Los Angeles or London with your Flash Pass."
{% endstep %}

{% step %}
**Enter Your IP Address**

Click **Auth** and enter your **public IPv4 address**.

IP requirements:

* Valid IPv4 format (e.g., `203.0.113.50`)
* Must be a **public** IP address
* Cannot be private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
  {% endstep %}

{% step %}
**Confirmation**

Upon successful authentication you will see:

"Authed IP: \[your IP]"\
"Node URL in #node-urls"
{% endstep %}
{% endstepper %}

## Changing Your Authenticated Location

Since Flash Pass only allows one IP total, to change your location:

1. Delete your current authentication using `!rpc delete`
2. Select your Flash Pass entry from the list
3. Click **Delete** to remove it
4. Run `!rpc auth` again
5. Select your new desired Flash location

## Creating Rentals with Flash Pass

Flash Pass holders can rent out their authentication slot. The pricing tier for Flash rentals is lower than Thor Pass rentals:

### Flash Pass Rental Pricing (Check discord server for recent pricing)

| Period  | IP Updates | Flash Pass Price |
| ------- | ---------- | ---------------- |
| 2 Hours | 2          | 0.25 SOL         |
| 1 Day   | 3          | 0.5 SOL          |
| 1 Week  | 5          | 1 SOL            |
| 1 Month | 9          | 2 SOL            |

### How to Create a Flash Rental

1. Run `!rpc auth` and select your Flash Pass
2. Choose a Flash-only location
3. Click **Rent** instead of **Auth**
4. Enter:
   * Renter's Discord ID
   * Rental period (`2H`, `1D`, `1W`, or `1M`)
   * Payment transaction ID

> **Note:** While you have an active rental, you cannot authenticate your own IP until the rental expires.

## Common Error Messages (FAQ)

<details>

<summary>"You can only authenticate to Los Angeles or London with your Flash Pass"</summary>

You selected a location that is not Flash-exclusive. Choose from: Los Angeles, London, Salt Lake City (Flash), or Frankfurt BSC.

</details>

<details>

<summary>"You already authenticated one IP address for your Flash Pass"</summary>

Flash Pass only allows one IP total. To authenticate a new IP:

1. Run `!rpc delete`
2. Remove your existing authentication
3. Try `!rpc auth` again

</details>

<details>

<summary>"This location is exclusive to Flash Pass only"</summary>

This message appears for non-Flash pass holders trying to access Flash locations. Only Flash Pass can access these locations.

</details>

## Flash Pass vs Other Passes

| Comparison     | Flash Pass              | Thor Pass Types         |
| -------------- | ----------------------- | ----------------------- |
| Locations      | Flash-only (4)          | All general (many)      |
| Max IPs        | 1 total                 | 4 to Unlimited          |
| Double IP      | No                      | Elite                   |
| Rental pricing | Lower tier              | Higher tier             |
| Best for       | Specific location needs | Flexible multi-location |

## Tips for Flash Pass Holders

1. **Choose wisely** - Since you only get one IP, pick the location closest to your infrastructure
2. **Check latency** - Before authenticating, verify which Flash location offers the best latency for your needs
3. **Consider rentals** - If you need temporary access to other locations, you can rent slots from other pass holders
4. **Plan changes** - Remember that changing locations requires deleting and re-authenticating

## Next Steps

* [Managing Your Authentication](/introduction/node-locations-and-access/authentication) - Learn to update and delete your IP
* [RPC Node Locations and URLs](/introduction/node-locations-and-access)
* [Renewal Guide](/introduction/renewals-and-subscriptions)
* [Rental System Guide](broken://pages/z2SVLlMfuQp9VpZZAzfE) - Rent additional locations from other users
* Troubleshooting - Solutions for common issues


# Node Locations and Access

All RPC node locations are subject to change depending on the usage and user request. New locations may be added and the current ones may be moved to another location.

{% hint style="info" %}
💡 Virginia Node is sponsored by **Zesty Solutions**. Buy a 30% discounted VPS from Zesty Store using **TN30GB** coupon code.
{% endhint %}

## Solana

<table><thead><tr><th>Location</th><th width="230.34375">URL</th><th width="220.96484375">WebSocket</th><th width="185.8828125">gRPC</th><th width="102.953125">RPS/TPS</th><th width="112.07421875">Availability</th><th>Bifrost</th></tr></thead><tbody><tr><td>Amsterdam, NL</td><td>http://rpc-ams.thornode.io/</td><td>ws://rpc-ams.thornode.io/</td><td>64.130.43.86:50051</td><td>400/200</td><td>All pass types</td><td>http://bifrost-ams.thornode.io/</td></tr><tr><td>Frankfurt, DE</td><td>http://rpc-de.thornode.io/</td><td>ws://rpc-de.thornode.io/</td><td>45.134.108.228:50051</td><td>400/200</td><td>All pass types</td><td>http://bifrost-de.thornode.io/</td></tr><tr><td>Frankfurt, DE (Elite Only)</td><td>http://rpc-elite-fr-27a519d7.thornode.io/</td><td>ws://rpc-elite-fr-27a519d7.thornode.io/</td><td>45.134.108.228:50051</td><td>600/300</td><td>Elite pass only</td><td>http://bifrost-de-elite.thornode.io/</td></tr><tr><td>London, UK</td><td>http://rpc-eu-west-2.thornode.io/</td><td>ws://rpc-eu-west-2.thornode.io/</td><td>64.130.63.7:50051</td><td>400/200</td><td>Flash Node Pass</td><td>http://bifrost-uk.thornode.io/</td></tr><tr><td>New York, USA</td><td>http://rpc-ny.thornode.io/</td><td>ws://rpc-ny.thornode.io/</td><td>64.130.37.200:50051</td><td>400/200</td><td>All pass types</td><td>http://bifrost-ny.thornode.io/</td></tr><tr><td>Salt Lake City, USA</td><td>http://rpc-slc1.thornode.io/</td><td>ws://rpc-slc1.thornode.io/</td><td>64.130.33.158:50051</td><td>600/300</td><td>All pass types</td><td>-</td></tr><tr><td>Tokyo, JP</td><td>http://ap-northeast-1.thornode.io/</td><td>ws://ap-northeast-1.thornode.io/</td><td>208.91.107.18:50051</td><td>600/300</td><td>All pass types</td><td>http://bifrost-jp.thornode.io/</td></tr><tr><td>Virginia, USA</td><td>http://rpc-va-zesty.thornode.io/</td><td>ws://rpc-va-zesty.thornode.io/</td><td>64.130.37.200:50051</td><td>400/200</td><td>All pass types</td><td>-</td></tr></tbody></table>

### Bifrost Services

| Location          | Services                         | Bifrost TPS |
| ----------------- | -------------------------------- | ----------- |
| Amsterdam         | bloXroute, Astralane, BlockRazor | 30          |
| Frankfurt         | bloXroute, Astralane, BlockRazor | 30          |
| Frankfurt (Elite) | bloXroute, Astralane, BlockRazor | 50          |
| London            | bloXroute, Astralane             | 30          |
| New York          | bloXroute, Astralane, BlockRazor | 30          |
| Tokyo             | bloXroute, Astralane, BlockRazor | 30          |

***

## Binance Smart Chain

| Location      | URL                                                             | WebSocket                    | RPS/TPS | Availability   |
| ------------- | --------------------------------------------------------------- | ---------------------------- | ------- | -------------- |
| Frankfurt, DE | [http://bsc-tokyo.thornode.io/](https://bsc-tokyo.thornode.io/) | ws\://bsc-tokyo.thornode.io/ | 300     | All pass types |

***

## Ethereum (Temporarily Unavailable)

| Location      | URL                                 | Availability   |
| ------------- | ----------------------------------- | -------------- |
| Virginia, USA | <https://rpc-ethereum.thornode.io/> | All pass types |

***

## Polygon (Temporarily Unavailable)

| Location      | URL                                    | Availability   |
| ------------- | -------------------------------------- | -------------- |
| Virginia, USA | <https://rpc-polygon-pos.thornode.io/> | All pass types |

***

{% hint style="info" %}
All endpoints support SSL connections:

* Replace `http://` with `https://`
* Replace `ws://` with `wss://`
  {% endhint %}

{% hint style="warning" %}
⚠️ Note: Web applications require HTTPS endpoints.
{% endhint %}

***

## Pass Differences

| Pass Type | IP Limit per Pass | Access                             | Notes                                                                   |
| --------- | ----------------- | ---------------------------------- | ----------------------------------------------------------------------- |
| Flash     | 1                 | Shared RPCs at available locations | Standard limits                                                         |
| Classic   | 4                 | Shared RPCs at available locations | Standard limits                                                         |
| Elite     | 4 + 1             | Shared + Elite-only endpoints      | 600 RPS / 300 TPS, 50 Bifrost TPS                                       |
| Prime     | 4 + Unlimited     | Shared + Dedicated RPC option      | Standalone servers, higher RPS. Contact Prime Pass holders for details. |


# Authentication

How to auth your IP?

## Authentication

{% stepper %}
{% step %}
Go to <https://dashboard.thornode.io/> to manage your endpoint and rentals access&#x20;
{% endstep %}
{% endstepper %}

### Rental Pricing

| Period  | IP Updates | Thor Pass Price |
| ------- | ---------- | --------------- |
| 2 Hours | 2          | 0.5 SOL         |
| 1 Day   | 3          | 1 SOL           |
| 1 Week  | 5          | 2 SOL           |
| 1 Month | 9          | 4 SOL           |

{% hint style="warning" %}
**Check discord server for the most recent pricing**
{% endhint %}

### Rental Requirements

* Renter must be a member of the Thor Labs Discord server
* Payment must be sent to one of your registered wallets
* Transaction must be recent and verifiable


# Renewals and Subscriptions

Renew your ThorLabs Node pass directly within Discord. Pricing varies based on pass type and timing.\
Follow the prompts of the discord bot [here](https://discord.com/channels/985679905868115988/1054356660753281096/1179904230958579844).

### Next Steps

* Pricing - See subscription prices for each tier
* How to Renew - Learn how to renew your pass subscription
* Burn Warning - Must read warning the keep your passes valid


# Pricing

## Pricing

### Flash Pass

| Price     | Duration | Notes                                 |
| --------- | -------- | ------------------------------------- |
| $100 USDC | 1 month  | Adds one month to current expiry date |

### Classic / Elite Pass

| Price     | Duration | Notes                                 |
| --------- | -------- | ------------------------------------- |
| $350 USDC | 1 month  | Adds one month to current expiry date |

### Prime Pass

| Price                                     | Duration | Notes                                 |
| ----------------------------------------- | -------- | ------------------------------------- |
| <p>Varying rates<br>Starts from 1000$</p> | 1 month  | Adds one month to current expiry date |

***

### No Holding

{% hint style="info" %}
Dedicated RPC node prices starts from 1890$ if you are not holding a Prime Pass NFT.\
Holding a prime pass provides around 1000$ discount on dedicated nodes with ThorLabs RPC node perks such as SWQoS
{% endhint %}

#### Check Pass Types

* Prime Pass
* ThorNode Passes
* FlashNode Pass


# How to Renew

{% stepper %}
{% step %}
**Initiate renewal**

Click the **"Renew"** button in Discord in the Renewal Channel
{% endstep %}

{% step %}
**Select pass(es)**

Choose your pass(es) from the **"Select your pass(es)"** dropdown menu.
{% endstep %}

{% step %}
**Choose duration**

Select your renewal period:

* 1 month
* 3 months
* 6 months
* 12 months

A confirmation window will display the total amount based on:

* Renewal period
* Pass type
* Current expiration date
  {% endstep %}

{% step %}
**Make payment**

Transfer the **exact renewal amount in USDC** to one of the following addresses:

```
DnbGBS2fDSuZX4efwJNPnjiseMz9G9TZrEipirGgA5CJ
```

or

```
thornode.sol
```

{% endstep %}

{% step %}
**Submit transaction ID**

Enter your payment transaction ID when prompted and submit.
{% endstep %}

{% step %}
**Confirmation**

Wait for bot validation and renewal confirmation.
{% endstep %}
{% endstepper %}

***

## Payment Requirements

| Requirement | Details                                                            |
| ----------- | ------------------------------------------------------------------ |
| Currency    | **USDC only**                                                      |
| Wallet      | <p>Must be registered at<br><https://verify-beta.thornode.io/></p> |
| Wait time   | \~1 minute for transaction confirmation                            |

{% hint style="danger" %}
Transactions from unregistered wallets will be rejected.
{% endhint %}

## Important Notes

* ✅ Multiple pass renewals can be processed with a single transaction
* ✅ Ensure the transaction ID is correct before submitting
* ✅ If validation fails, retry after a moment before contacting support
* ❌ Use only your own registered wallet


# Refund Policy

{% hint style="danger" %}
**No refunds on renewals.** Refunds are only processed for user mistakes (e.g., incorrect payment amount, wrong wallet).
{% endhint %}

| Condition       | Policy                                               |
| --------------- | ---------------------------------------------------- |
| Processing time | Up to 2 weeks                                        |
| User error fee  | $50 processing fee                                   |
| Minimum amount  | Payments under $100 are **not eligible** for refunds |


# Burn Warning

| Pass Type      | Burn Threshold                     |
| -------------- | ---------------------------------- |
| ThorNode Pass  | Inactive for **more than 1 month** |
| FlashNode Pass | Inactive for **more than 1 week**  |
| Prime Pass     | No burning or inactivity fee       |

{% hint style="danger" %}
**Inactive passes exceeding these thresholds are subject to burn. Contact support for assistance.**
{% endhint %}

### Strategies to keep your pass inactive but safe aka "freezing"

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-1cd64fa7a836f980ba1e6c4afefb12ffe8be91d1%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-077605ceaedf34b2819748d5bee745c6ba0730eb%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

> *AI-generated images for better clarification.*


# Asgard Vision

**Global Load Balancer for Solana**

***

## The All-Seeing Infrastructure

In Norse mythology, Asgard's watchmen could see across all nine realms. **Asgard Vision** brings this power to Solana—a globally distributed system that ensures your transactions reach the blockchain with unmatched speed and reliability.

***

## What We Do

You send one transaction. We make sure it lands.

Asgard Vision takes your transaction and broadcasts it across a global network of nodes and validators simultaneously. More paths = higher chance of success.

**Simple as that.**

***

## How It Works

```
                           ┌────────────────┐
                           │  Asgard Vision │
        Your Transaction ─►│                │
                           │  Load Balancer │
                           └───────┬────────┘
                                   │
        ┌──────────────────────────┼──────────────────────────┐
        │                          │                          │
        ▼                          ▼                          ▼
┌───────────────┐          ┌───────────────┐          ┌───────────────┐
│   RPC Nodes   │          │  TXN Senders  │          │    SWQoS      │
│               │          │     Swarm     │          │  Validators   │
│   ThorLabs    │          │  Lightweight  │          │  4M SOL Stake │
│   Endpoints   │          │    Clients    │          │  Partner Net  │
└───────┬───────┘          └───────┬───────┘          └───────┬───────┘
        │                          │                          │
        └──────────────────────────┴──────────────────────────┘
                                   │
                                   ▼
                         ┌───────────────────┐
                         │   Solana Network  │
                         └───────────────────┘
```

***

## Three Network Tiers

{% columns %}
{% column %}
**RPC Nodes**

ThorLabs' RPC node network, distributed globally for consistent connectivity.
{% endcolumn %}

{% column %}
**TXN Senders**

A large swarm of lightweight Solana clients distributed around the world for fast, direct transaction delivery.
{% endcolumn %}

{% column %}
**SWQoS Partner Validators**

Our exclusive network of partner validators with **4 million staked SOL** — giving your transactions priority access to block leaders worldwide.
{% endcolumn %}
{% endcolumns %}

***

## Why 4 Million SOL Matters

Solana uses stake-weighted quality of service. More stake = more priority.

Our partner validators collectively hold **4,000,000 SOL** in stake. When your transaction goes through our SWQoS network, it gets the priority treatment that stake power provides.

{% hint style="info" %}
Stake-weighted QoS on Solana means leader validators allocate transaction bandwidth proportionally to stake — more stake, more guaranteed throughput.
{% endhint %}

***

## What You Get

### Instant Response

Your app doesn't wait. We acknowledge transaction result immediately and handle the rest.

### Multi-Path Delivery

One transaction, many paths. We broadcast across all three network tiers simultaneously.

### Global Reach

Nodes and validators strategically placed worldwide for lowest latency.

### Self-Healing Network

We continuously monitor all nodes. Slow or failing nodes are automatically bypassed.

### Real-Time Visibility

Track success rates and performance across your entire transaction flow.

***

## The Bottom Line

| ❌ Without Asgard          | ✅ With Asgard Vision           |
| ------------------------- | ------------------------------ |
| ❌ Single point of failure | ✅ Multi-path redundancy        |
| ❌ Hope it lands           | ✅ Maximize landing probability |
| ❌ Wait for confirmation   | ✅ Instant response             |
| ❌ Public congestion       | ✅ Direct validator paths       |
| ❌ No visibility           | ✅ Real-time monitoring         |

***

## Built For

* **Traders** — Speed and reliability when it matters most
* **DeFi Apps** — Solid transaction backbone at scale
* **Bots** — Consistent performance for automated systems
* **Anyone** — Who needs transactions to land, fast

***

> **4 Million SOL Stake Power**
>
> **Global Reach**
>
> **Instant Execution**

***

*Asgard Vision — All-seeing. All-reaching. All-delivering.*


# Bifrost API

High-performance Solana transaction proxy connecting you to leading MEV infrastructure providers through a single, unified API.

## Why Bifrost?

* **One API, Multiple Providers** - Access Bloxroute, Astralane, BlockRazor and Lunar Lander through unified endpoints
* **MEV Protection** - Built-in support for submissions across providers
* **Advanced Strategies** - Bundle, batch, and snipe capabilities for sophisticated trading

## Supported Providers

### Bloxroute

Industry-leading MEV infrastructure with batch processing, Paladin protection, and snipe capabilities.

### Astralane

Advanced bundle and ideal pair submissions with MEV protection.

### BlockRazor

Fast, reliable transaction submission with global coverage.

#### Lunar Lander

Advanced and reliable transaction submission with batch processing.

## Quick Example

{% code title="curl" %}

```bash
curl -X POST https://your-bifrost-endpoint/bloxroute \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": ["your-base64-transaction"]
  }'
```

{% endcode %}

## Next Steps

* View all endpoints
* Bloxroute API
* Astralane API
* BlockRazor API
* Lunar Lander
* Additional Optimizations


# Endpoints

Overview of the public Bifrost routes for transaction submission.

## Root

| Endpoint | Description                     |
| -------- | ------------------------------- |
| `GET /`  | Lists public transaction routes |
| `POST /` | Not supported                   |

## Standard Provider Routes

| Endpoint            | Provider    | Use for                                     |
| ------------------- | ----------- | ------------------------------------------- |
| `POST /bloxroute`   | bloXroute   | Single transaction submit                   |
| `POST /astralane`   | Astralane   | Generic Astralane JSON-RPC requests         |
| `POST /blockrazor`  | BlockRazor  | v1 JSON or v2 plain-text transaction submit |
| `POST /lunarlander` | LunarLander | Single transaction submit                   |

## bloXroute Routes

| Endpoint                     | Use for                   |
| ---------------------------- | ------------------------- |
| `POST /bloxroute`            | Single transaction submit |
| `POST /blxrt-submit-batch`   | Batch submit              |
| `POST /blxrt-submit-paladin` | Paladin submit            |
| `POST /blxrt-submit-snipe`   | Snipe submit              |

## Astralane Routes

All Astralane transaction routes use Astralane's JSON-RPC request body.

| Endpoint                     | Use for                                                         |
| ---------------------------- | --------------------------------------------------------------- |
| `POST /astralane`            | `sendTransaction` and other standard Astralane JSON-RPC methods |
| `POST /astrln-submit-bundle` | `sendBundle`                                                    |
| `POST /astrln-submit-batch`  | `sendBatch`                                                     |
| `POST /astrln-submit-ideal`  | `sendIdeal`                                                     |
| `GET /astrln-get-nonce`      | `getNonce`                                                      |
| `POST /astrln-get-nonce`     | `getNonce`                                                      |

## LunarLander Routes

| Endpoint                    | Use for                   |
| --------------------------- | ------------------------- |
| `POST /lunarlander`         | Single transaction submit |
| `POST /lunar-submit-bundle` | Bundle submit             |
| `POST /lunar-submit-batch`  | Batch submit              |


# Astralane

Use these routes when you want Astralane's JSON-RPC API. Send the same JSON-RPC body shown in Astralane's docs.

Reference:

* <https://astralane.gitbook.io/docs/low-latency/submit-transactions>

## Routes

| Route                        | Use for                             |
| ---------------------------- | ----------------------------------- |
| `POST /astralane`            | Generic Astralane JSON-RPC requests |
| `POST /astrln-submit-bundle` | `sendBundle`                        |
| `POST /astrln-submit-batch`  | `sendBatch`                         |
| `POST /astrln-submit-ideal`  | `sendIdeal`                         |
| `GET /astrln-get-nonce`      | `getNonce`                          |
| `POST /astrln-get-nonce`     | `getNonce`                          |

Supported query params: `api-key`, `mev-protect`, `swqos-only`.

If your Bifrost deployment already has Astralane credentials configured, you can usually omit `api-key`.

`sendPaladin` is not supported.

## Tip Addresses

Current Astralane tip addresses recognized by this Bifrost deployment:

* `astrazznxsGUhWShqgNtAdfrzP2G83DzcWVJDxwV9bF`
* `astra4uejePWneqNaJKuFFA8oonqCE1sqF6b45kDMZm`
* `astra9xWY93QyfG6yM8zwsKsRodscjQ2uU2HKNL5prk`
* `astraRVUuTHjpwEVvNBeQEgwYx9w9CFyfxjYoobCZhL`
* `astraEJ2fEj8Xmy6KLG7B3VfbKfsHXhHrNdCQx7iGJK`
* `astraubkDw81n4LuutzSQ8uzHCv4BhPVhfvTcYv8SKC`
* `astraZW5GLFefxNPAatceHhYjfA1ciq9gvfEg2S47xk`
* `astrawVNP4xDBKT7rAdxrLYiTSTdqtUr63fSMduivXK`

Use one of these when Astralane requires a valid tip in the transaction.

## sendTransaction

Use `POST /astralane` with the official Astralane JSON-RPC body:

{% code title="sendTransaction JSON-RPC" %}

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendTransaction",
  "params": [
    "<base64-transaction>",
    {
      "encoding": "base64",
      "skipPreflight": true
    },
    {
      "mevProtect": true
    }
  ]
}
```

{% endcode %}

Common options:

* Query params: `api-key`, `mev-protect`, `swqos-only`
* `params[1].encoding`: recommended `base64`
* `params[1].skipPreflight`: recommended `true`
* `params[2].mevProtect`: optional

## sendBundle

Use `POST /astrln-submit-bundle` or `POST /astralane` with `method: "sendBundle"`:

{% code title="sendBundle JSON-RPC" %}

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendBundle",
  "params": [
    [
      "<base64-transaction-1>",
      "<base64-transaction-2>"
    ],
    {
      "encoding": "base64",
      "mevProtect": true,
      "revertProtection": false
    }
  ]
}
```

{% endcode %}

* Maximum 4 transactions
* `revertProtection` is optional

## sendBatch

Use `POST /astrln-submit-batch` or `POST /astralane` with `method: "sendBatch"`:

{% code title="sendBatch JSON-RPC" %}

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendBatch",
  "params": [
    [
      "<base64-transaction-1>",
      "<base64-transaction-2>"
    ],
    {
      "mevProtect": true
    }
  ]
}
```

{% endcode %}

* Maximum 25 transactions
* Astralane documents `sendBatch` as available on request
* Requirement: every transaction must include a valid Astralane tip

## sendIdeal

Use `POST /astrln-submit-ideal` or `POST /astralane` with `method: "sendIdeal"`:

{% code title="sendIdeal JSON-RPC" %}

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendIdeal",
  "params": [
    [
      "<base64-high-tip-low-fee-transaction>",
      "<base64-low-tip-high-fee-transaction>"
    ]
  ]
}
```

{% endcode %}

`sendIdeal` expects exactly 2 transactions.

## getNonce

Use `GET /astrln-get-nonce` or `POST /astrln-get-nonce` when you need Astralane's `getNonce` flow for `sendIdeal`.

Example JSON-RPC call forwarded upstream:

{% code title="getNonce JSON-RPC" %}

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getNonce",
  "params": [
    "<api-key>"
  ]
}
```

{% endcode %}

Example helper response:

{% code title="getNonce response" %}

```json
{
  "nonce": "<hash>",
  "nonceAccount": "<pubkey>",
  "nonceAuthority": "<pubkey>"
}
```

{% endcode %}


# Blockrazor

Use `POST /blockrazor` with the same request shape described in the official BlockRazor docs. Bifrost supports both official BlockRazor HTTP contracts:

* v1 JSON body: `/sendTransaction`
* v2 low-latency plain text body: `/v2/sendTransaction`

Reference:

* <https://blockrazor.gitbook.io/blockrazor/transaction-submission/fast/solana/send-transaction>
* <https://blockrazor.gitbook.io/blockrazor/transaction-submission/fast/solana/send-transaction-v2>

## Tip Addresses

Current BlockRazor tip addresses recognized by this Bifrost deployment:

* `FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9`
* `6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG`
* `A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22`
* `Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm`
* `68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o`
* `4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9`
* `B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM`
* `5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf`
* `5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61`
* `295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV`
* `EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK`
* `BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6`
* `Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq`
* `AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S`

## v1 JSON Contract

Send `Content-Type: application/json` and the upstream JSON body:

```json
{
  "transaction": "<base64-transaction>",
  "mode": "fast",
  "safeWindow": 3,
  "revertProtection": false
}
```

Fields:

* `transaction`: required, signed transaction string
* `mode`: optional, `fast` or `sandwichMitigation`
* `safeWindow`: optional, `3-13`
* `revertProtection`: optional boolean
* Auth: upstream v1 uses the `apikey` HTTP header

Example:

```bash
curl -X POST https://your-bifrost-endpoint/blockrazor \
  -H "Content-Type: application/json" \
  -H "apikey: your-blockrazor-api-key" \
  -d '{
    "transaction": "your-base64-transaction",
    "mode": "fast",
    "revertProtection": false
  }'
```

## v2 Plain Text Contract

Send `Content-Type: text/plain` and put request parameters in the query string, exactly as BlockRazor v2 expects.

```bash
curl -X POST 'https://your-bifrost-endpoint/blockrazor?mode=fast&revertProtection=true' \
  -H "Content-Type: text/plain" \
  -d 'your-base64-transaction'
```

Notes:

* Upstream v2 expects Base64 transaction text in the body
* Upstream v2 only permits `Content-Type: text/plain`
* Query params such as `mode`, `safeWindow`, `revertProtection`, and `auth` are forwarded upstream
* If your Bifrost deployment already has BlockRazor credentials configured, you can usually omit `auth`

## Response

* For v1, BlockRazor returns JSON with a `signature` field.
* For v2, BlockRazor returns standard HTTP status codes as documented upstream.


# Bloxroute

Use these routes when you want bloXroute's HTTP transaction submission API. Send the same JSON body shown in bloXroute's docs.

Reference:

* <https://github.com/bloXroute-Labs/solana-trader-proto/blob/develop/proto/api.proto>

***

## Routes

| Route                        | Use for                   |
| ---------------------------- | ------------------------- |
| `POST /bloxroute`            | Single transaction submit |
| `POST /blxrt-submit-batch`   | Batch submit              |
| `POST /blxrt-submit-paladin` | Paladin submit            |
| `POST /blxrt-submit-snipe`   | Snipe submit              |

***

## Tip Addresses

Current bloXroute tip addresses recognized by this Bifrost deployment:

* `HWEoBxYs7ssKuudEjzjmpfJVX7Dvi7wescFsVx2L5yoY`
* `95cfoy472fcQHaw4tPGBTKpn6ZQnfEPfBgDQx6gcRmRg`
* `3UQUKjhMKaY2S6bjcQD6yHB7utcZt5bfarRCmctpRtUd`
* `FogxVNs6Mm2w9rnGL1vkARSwJxvLE8mujTv3LK8RnUhF`
* **Dedicated**: `EhRZX8iNrCRzEJ97tU4d3vC7JHkdsh2BWuT8yYbiseCe`

If you specifically want the Bifrost-managed dedicated bloXroute tip address, use `EhRZX8iNrCRzEJ97tU4d3vC7JHkdsh2BWuT8yYbiseCe`.

***

## submit

Use `POST /bloxroute` with the official bloXroute submit body:

{% code title="Request body" %}

```json
{
  "transaction": {
    "content": "<base64-transaction>"
  },
  "skipPreFlight": true,
  "frontRunningProtection": true,
  "tip": 1000000,
  "useStakedRPCs": true,
  "fastBestEffort": false,
  "allowBackRun": false,
  "submitProtection": "SP_HIGH",
  "revertProtection": false
}
```

{% endcode %}

Common fields:

* `transaction.content`: required
* `skipPreFlight`
* `frontRunningProtection`
* `tip`
* `useStakedRPCs`
* `fastBestEffort`
* `allowBackRun`
* `revenueAddress`
* `submitProtection`: `SP_LOW`, `SP_MEDIUM`, `SP_HIGH`
* `revertProtection`

***

## submit-batch

Use `POST /blxrt-submit-batch`:

{% code title="Request body" %}

```json
{
  "entries": [
    {
      "transaction": {
        "content": "<base64-transaction-1>",
        "isCleanup": false
      },
      "skipPreFlight": true
    },
    {
      "transaction": {
        "content": "<base64-transaction-2>",
        "isCleanup": false
      },
      "skipPreFlight": true
    }
  ],
  "submitStrategy": "P_SUBMIT_ALL",
  "useBundle": false,
  "frontRunningProtection": true,
  "submitProtection": "SP_HIGH"
}
```

{% endcode %}

Useful enums from the official proto:

* `submitStrategy`: `P_UKNOWN`, `P_SUBMIT_ALL`, `P_ABORT_ON_FIRST_ERROR`, `P_WAIT_FOR_CONFIRMATION`
* `submitProtection`: `SP_LOW`, `SP_MEDIUM`, `SP_HIGH`

***

## submit-paladin

Use `POST /blxrt-submit-paladin`:

{% code title="Request body" %}

```json
{
  "transaction": {
    "content": "<base64-transaction>"
  },
  "revertProtection": false
}
```

{% endcode %}

***

## submit-snipe

Use `POST /blxrt-submit-snipe`:

{% code title="Request body" %}

```json
{
  "entries": [
    {
      "transaction": {
        "content": "<base64-transaction-jito>"
      },
      "skipPreFlight": true
    },
    {
      "transaction": {
        "content": "<base64-transaction-bloxroute>"
      },
      "skipPreFlight": true
    }
  ],
  "useStakedRPCs": true
}
```

{% endcode %}


# Lunarlander

Use these routes when you want HelloMoon Lunar Lander. Send the same request body described in HelloMoon's docs.

Reference:

* <https://docs.hellomoon.io/reference/lunar-lander>
* <https://docs.hellomoon.io/reference/send-bundle-api>
* <https://docs.hellomoon.io/reference/batch-send-api>

## Tip Addresses

Current LunarLander tip addresses recognized by this Bifrost deployment:

* `moon17L6BgxXRX5uHKudAmqVF96xia9h8ygcmG2sL3F`
* `moon26Sek222Md7ZydcAGxoKG832DK36CkLrS3PQY4c`
* `moon7fwyajcVstMoBnVy7UBcTx87SBtNoGGAaH2Cb8V`
* `moonBtH9HvLHjLqi9ivyrMVKgFUsSfrz9BwQ9khhn1u`
* `moonCJg8476LNFLptX1qrK8PdRsA1HD1R6XWyu9MB93`
* `moonF2sz7qwAtdETnrgxNbjonnhGGjd6r4W4UC9284s`
* `moonKfftMiGSak3cezvhEqvkPSzwrmQxQHXuspC96yj`
* `moonQBUKBpkifLcTd78bfxxt4PYLwmJ5admLW6cBBs8`
* `moonXwpKwoVkMegt5Bc776cSW793X1irL5hHV1vJ3JA`
* `moonZ6u9E2fgk6eWd82621eLPHt9zuJuYECXAYjMY1C`

Dedicated LunarLander tip addresses (Bifrost-managed):

* `tr7c9YdFm5pTx8xVp8XMRPrAFGSND7xmumTqPPJchHe`
* `trHMAN5tkHbzyvFobwjCx1G2tcKTo1ymVhog1ZVBqYD`
* `trWZmScJ2813eMwQLDpc96yf15mtFyXsafpP9NAfjdQ`
* `trdMqFh3cPLdweYmD9tLp8NeMZ6EJUhiDHr8XrqmuJN`
* `trzB8VqS6U3pG7CLjCoLTRvLw3i2ZEsjfAyhZmM3B27`

If you specifically want the Bifrost-managed dedicated LunarLander tip addresses, use one of the `tr...` addresses above.

## Send Transaction

Use `POST /lunarlander` for a single transaction.

Request Body

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendTransaction",
  "params": [
    "<base64-transaction>",
    {
      "encoding": "base64"
    }
  ]
}
```

| Field                | Type   | Required | Description                       |
| -------------------- | ------ | -------- | --------------------------------- |
| `params[0]`          | string | Yes      | Base64-encoded signed transaction |
| `params[1].encoding` | string | No       | `base64` (default)                |

Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<transaction-signature>"
}
```

## Bundle Submission

Use `POST /lunar-submit-bundle` for a bundle of up to 4 transactions.

Request Body

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendBundle",
  "params": [
    [
      "<base64-transaction-1>",
      "<base64-transaction-2>"
    ],
    {
      "encoding": "base64"
    }
  ]
}
```

| Field                | Type      | Required | Description                         |
| -------------------- | --------- | -------- | ----------------------------------- |
| `params[0]`          | string\[] | Yes      | Base64-encoded transactions (max 4) |
| `params[1].encoding` | string    | No       | `base64` (default)                  |

Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<bundle-id>"
}
```

## Batch Submission

Use `POST /lunar-submit-batch` for LunarLander's binary batch API.

Headers

| Header         | Value                      |
| -------------- | -------------------------- |
| `Content-Type` | `application/octet-stream` |

If your Bifrost deployment already has HelloMoon credentials configured, you usually do not need to send auth yourself.

Request Body (Binary)

Wire format uses repeating `[u16_be_length][raw_tx_bytes]` segments:

```
[2 bytes: tx1 length (big-endian)][tx1 bytes]
[2 bytes: tx2 length (big-endian)][tx2 bytes]
...
```

Maximum 16 transactions per batch. Each transaction payload must be 66-1232 bytes.

Response

```json
{
  "attempted": 2,
  "accepted": 2,
  "rejected": 0,
  "parse_error": null
}
```


# Additional Optimisations

Use this route when you want a lightweight request for keep-alive and connection warm-up.

The main optimization is not the route itself. The real gain comes from reusing the same persistent HTTP connection to Bifrost instead of opening a new connection for every transaction.

`/healthz` exists as a cheap request you can send on that same connection when you want to keep it active between transaction submissions.

## Why It Helps

* Reduces the chance that your next transaction pays the full connection setup cost
* Lets you keep the client-to-Bifrost connection active without sending a transaction
* Gives you a lightweight probe for reachability on the public listener

`/healthz` does not forward anything to upstream providers. It only checks that Bifrost itself is reachable.

{% hint style="warning" %}
Important

`/healthz` only helps if your client reuses the same HTTP client / connection pool.

If your client opens a fresh connection for every call, `/healthz` does not keep the later transaction connection warm.
{% endhint %}

Recommended pattern:

* Keep one long-lived HTTP client
* Enable normal keep-alive / connection pooling
* Send transactions over that reused client
* Optionally call `/healthz` between bursts when you want to keep the connection active

{% hint style="warning" %}
Important

The public Thor-Gateway listener currently uses an idle keep-alive timeout of about `30 seconds` .

If you want to keep the same client-to-Bifrost HTTP connection open, send a lightweight `GET /healthz` or `HEAD /healthz` roughly every `20-25 seconds` .
{% endhint %}

## Routes

| Route           | Use for                                             |
| --------------- | --------------------------------------------------- |
| `GET /healthz`  | Lightweight keep-alive and reachability check       |
| `HEAD /healthz` | Same as `GET /healthz`, but without a response body |

## GET /healthz

Example request:

```bash
curl https://your-bifrost-endpoint/healthz
```

Example response:

```json
{
  "status": "ok",
  "service": "Bifrost Transaction Proxy"
}
```

## HEAD /healthz

Example request:

```bash
curl -I https://your-bifrost-endpoint/healthz
```

Expected behavior:

* Returns `200 OK`
* Returns no response body
* Useful when you want the lightest possible keep-alive probe


# ThorPulse NATS

ThorPulse NATS documentation

ThorPulse is a high-performance Solana data streaming service that delivers real-time blockchain data with ultra-low latency.

## What is ThorPulse?

ThorPulse consumes Solana shreds from ThorShredStream, processes them through a high-performance pipeline, and delivers parsed data (slots, transactions, accounts, entries, blocks) over an embedded NATS server using Yellowstone-compatible protobuf format.

Key Features:

* Ultra-low latency data delivery (sub-millisecond)
* Yellowstone-compatible protobuf format
* Server-side filtering with SubscribeRequest
* JWT authentication with subscription tiers

## Quick Links

* Quick Start Guide - Get connected in 5 minutes
* Authentication - JWT credentials and tier system
* Subscription Reference - How to subscribe to data
* Topic Reference - Complete topic schema

## SDKs

{% tabs %}
{% tab title="Go" %}
Installation:

{% code title="Install" %}

```bash
go get github.com/thorlabsDev/thorpulse-go
```

{% endcode %}

Example usage:

{% code title="example.go" %}

```go
import "github.com/thorlabsDev/thorpulse-go"

c, err := client.Connect(client.DefaultConfig("nats://ny-rpc.thornode.io:4222"))
defer c.Close()

c.SubscribeSlots(func(slot *client.SlotUpdate) {
    fmt.Printf("Slot %d: %s\n", slot.Slot, slot.Status)
})
```

{% endcode %}

More: [Go SDK Documentation](https://github.com/thorlabsDev/thorpulse-go)
{% endtab %}

{% tab title="Rust" %}
Add to Cargo.toml:

{% code title="Cargo.toml" %}

```toml
[dependencies]
thorpulse-client = "0.1"
```

{% endcode %}

Example usage:

{% code title="example.rs" %}

```rust
use thorpulse_client::ThorPulseClient;

let client = ThorPulseClient::connect_simple("nats://ny-rpc.thornode.io:4222").await?;
let mut slots = client.subscribe_slots().await?;

while let Some(slot) = slots.recv().await {
    println!("Slot {}: {:?}", slot?.slot, slot?.status);
}
```

{% endcode %}

More: [Rust SDK Documentation](https://crates.io/crates/thorpulse-client)
{% endtab %}
{% endtabs %}

## Documentation Structure

```
docs/
├── getting-started/
│   ├── quickstart.md          # 5-minute quick start
│   └── authentication.md      # JWT credentials and tiers
├── reference/
│   ├── topics.md              # Complete topic schema
│   └── subscriptions.md       # Subscription patterns and filters
```

## Data Types

ThorPulse streams the following Solana data types:

| Type         | Description           | Topic Pattern                 |
| ------------ | --------------------- | ----------------------------- |
| Slots        | Slot status updates   | `slots.{slot}.{status}`       |
| Transactions | Parsed transactions   | `txs.program.{id}.{slot}`     |
| Accounts     | Account state changes | `accounts.pubkey.{pk}.{slot}` |
| Entries      | Ledger entries        | `entries.{slot}.{index}`      |
| Blocks       | Complete blocks       | `blocks.{slot}`               |

## Getting Help

* Contact Thor Labs for support
* See SDK documentation for working code examples

## License

ThorPulse is licensed under the MIT License.


# Getting started

Get connected to ThorPulse and receiving Solana data in 5 minutes.\ <br>

## Next Steps

* Authentication - JWT credentials and tiers
* Quickstart - Start streaming in minutes


# Authentication

ThorPulse uses NATS JWT authentication to secure access and enforce subscription limits based on your tier.

## Subscription Tiers

| Tier  | Subscriptions | Available Topics |
| ----- | ------------- | ---------------- |
| Flash | 100           | all topics       |
| Thor  | 250           | all topics       |
| Prime | Unlimited     | all topics       |

## Getting Credentials

Contact Thor Labs to obtain your credentials. You will receive:

* A `.creds` file containing your JWT and NKey seed
* The NATS server URL (e.g., `nats://ny-rpc.thornode.io:4223`)

## Credentials File Format

A `.creds` file looks like:

```
-----BEGIN NATS USER JWT-----
eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJBQk...
------END NATS USER JWT------

************************* IMPORTANT *************************
NKEY Seed printed below can be used to sign and prove identity.
NKEYs are sensitive and should be treated as secrets.

-----BEGIN USER NKEY SEED-----
SUAM4QXBNMN3FHPJQ7VUPNKJ3HZ3TJWMHFQ4KJ6UJKA7AB2CD3EF...
------END USER NKEY SEED------

*************************************************************
```

**Keep this file secure!** The NKey seed can be used to authenticate as you.

## Using Credentials

### Go SDK

```go
package main

import (
    "github.com/thorlabsDev/thorpulse-go"
    "log"
)

func main() {
    // Option 1: Load from file
    creds, err := client.LoadCredentials("user.creds")
    if err != nil {
        log.Fatal(err)
    }

    c, err := client.New(
        client.WithURL("nats://ny-rpc.thornode.io:4223"),
        client.WithCredentials(creds),
    )

    // Option 2: Load directly
    c, err := client.New(
        client.WithURL("nats://ny-rpc.thornode.io:4223"),
        client.WithCredentialsFile("user.creds"),
    )

    // Option 3: From environment
    // Set THORPULSE_CREDS_FILE=/path/to/user.creds
    c, err := client.New(
        client.WithURL("nats://ny-rpc.thornode.io:4223"),
        client.WithCredentialsFromEnv(),
    )
}
```

### Rust SDK

```rust
use thorpulse_client::{ThorPulseClient, Credentials, Config};
use std::time::Duration;

#[tokio::main]
async fn main() -> thorpulse_client::Result<()> {
    // Load credentials
    let creds = Credentials::from_file("user.creds")?;

    // Connect with credentials
    let config = Config::builder("nats://ny-rpc.thornode.io:4223")
        .name("my-app")
        .credentials(creds)
        .build();

    let client = ThorPulseClient::connect(config).await?;

    // Use client...
    Ok(())
}
```

## Environment Variables

Configure authentication via environment variables:

| Variable               | Description              |
| ---------------------- | ------------------------ |
| `THORPULSE_CREDS_FILE` | Path to credentials file |
| `THORPULSE_JWT`        | JWT token directly       |
| `THORPULSE_NKEY_SEED`  | NKey seed directly       |

## Troubleshooting

<details>

<summary>"authorization violation"</summary>

Your user doesn't have permission to subscribe to the requested topic. Check:

* Your tier allows the topic type
* You haven't exceeded your subscription limit
* The topic pattern is valid

</details>

<details>

<summary>"signature verification failed"</summary>

The NKey seed doesn't match the JWT. Ensure you're using the correct credentials file.

</details>

<details>

<summary>"timeout waiting for pong"</summary>

The server didn't respond. Check:

* The NATS URL is correct
* Network connectivity to the server
* Firewall rules allow outbound to port 4223

</details>

<details>

<summary>"invalid credentials file"</summary>

The credentials file is malformed. Verify:

* Both JWT and NKEY SEED sections are present
* No extra whitespace or corruption
* File encoding is UTF-8

</details>

## Security Best Practices

{% stepper %}
{% step %}
**Never commit credentials to git**

Add `*.creds` to `.gitignore`.
{% endstep %}

{% step %}
**Use environment variables in production**

Don't hardcode paths; use environment variables instead.
{% endstep %}

{% step %}
**Rotate credentials regularly**

Request new credentials periodically.
{% endstep %}

{% step %}
**Restrict file permissions**

Use `chmod 600 user.creds` to limit file access.
{% endstep %}

{% step %}
**Use separate credentials per environment**

Use different credentials for dev/staging/prod.
{% endstep %}
{% endstepper %}


# Quickstart

Get connected to ThorPulse and receiving Solana data in 5 minutes.

{% hint style="info" %}
Prerequisites

* Go 1.21 or later
* ThorPulse credentials (JWT + Seed)
  {% endhint %}

{% stepper %}
{% step %}
**Get Your Credentials**

Contact Thor Labs to obtain your credentials file (`thorpulse.creds`). This file contains your JWT token and NKey seed for authentication.
{% endstep %}

{% step %}
**Install the SDK**

```bash
go get github.com/thorpulse/thorpulse-go
```

{% endstep %}

{% step %}
**Connect and Subscribe**

Create a file `main.go`:

{% code title="main.go" %}

```go
package main

import (
    "fmt"
    "log"
    "os"
    "os/signal"
    "syscall"

    "github.com/thorlabsDev/thorpulse-go"
)

func main() {
    // Connect using credentials file
    cfg := client.ConfigWithCredsFile(
        "nats://ny-rpc.thornode.io:4223",
        "thorpulse.creds",
    )

    // Add connection callbacks (optional)
    cfg.OnDisconnect = func(err error) {
        log.Printf("Disconnected: %v", err)
    }
    cfg.OnReconnect = func() {
        log.Println("Reconnected!")
    }

    // Connect
    c, err := client.Connect(cfg)
    if err != nil {
        log.Fatal("Connection failed:", err)
    }
    defer c.Close()

    fmt.Println("Connected to ThorPulse!")

    // Subscribe to slot updates
    err = c.SubscribeSlots(func(slot *client.SlotUpdate) {
        fmt.Printf("Slot %d: %s\n", slot.Slot, slot.Status)
    })
    if err != nil {
        log.Fatal("Subscribe failed:", err)
    }

    fmt.Println("Subscribed to slots. Press Ctrl+C to exit.")

    // Wait for interrupt signal
    sig := make(chan os.Signal, 1)
    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
    <-sig

    fmt.Println("Shutting down...")
}
```

{% endcode %}
{% endstep %}

{% step %}
**Run**

```bash
go run main.go
```

Expected output:

```
Connected to ThorPulse!
Subscribed to slots. Press Ctrl+C to exit.
Slot 234567890: first_shred_received
Slot 234567890: completed
Slot 234567890: processed
Slot 234567890: confirmed
Slot 234567891: first_shred_received
...
```

{% endstep %}

{% step %}
**Try Other Subscriptions**

**Subscribe to Transactions**

```go
// All transactions for a program
err = c.SubscribeProgram("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    func(tx *client.TransactionUpdate) {
        fmt.Printf("TX: %x (slot %d)\n", tx.Signature[:8], tx.Slot)
    })

// Transactions involving a specific account
err = c.SubscribeAccount("So11111111111111111111111111111111111111112",
    func(tx *client.TransactionUpdate) {
        fmt.Printf("SOL TX: %x\n", tx.Signature[:8])
    })
```

**Subscribe to Account Updates**

```go
err = c.SubscribeAccounts(func(acc *client.AccountUpdate) {
    fmt.Printf("Account %x updated: %d lamports\n",
        acc.Pubkey[:8], acc.Lamports)
})
```

**Use Channel-Based API**

```go
import "context"

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

slots, err := c.SlotChan(ctx, 100)
if err != nil {
    log.Fatal(err)
}

for slot := range slots {
    fmt.Printf("Slot %d\n", slot.Slot)
}
```

{% endstep %}
{% endstepper %}

## Next Steps

* Authentication Guide - Understand JWT credentials
* Topic Reference - Complete topic schema
* Subscription Reference - Subscription patterns and filters

## Troubleshooting

<details>

<summary>Connection Refused</summary>

* Verify the server URL is correct
* Check network connectivity
* Ensure the ThorPulse server is running

</details>

<details>

<summary>Authentication Failed</summary>

* Verify credentials file exists and is readable
* Check credentials haven't expired (30-day user JWT expiry)
* Ensure account is active

</details>

<details>

<summary>No Data Received</summary>

* Check subscription topic pattern
* Verify tier allows access to requested topics
* Ensure the Solana network is producing blocks

</details>


# Reference

ThorPulse publishes data to NATS topics following a hierarchical naming convention.

## Next Steps

* Subscriptions - Subscribe to data streams
* Topics - NATS topic patterns


# Subscriptions

This document covers all subscription methods and filtering options available in ThorPulse.

## Simple Subscriptions

Simple subscriptions connect directly to NATS topic patterns. They're easy to use but don't support server-side filtering.

### Slots

{% code title="Go" %}

```go
// All slot updates
slots := client.SubscribeSlotsChannel(ctx)

// Confirmed slots only
slots := client.SubscribeSlotsConfirmedChannel(ctx)
```

{% endcode %}

Topic patterns:

* `slots.>` - All slot events
* `slots.*.confirmed` - Confirmed slots only
* `slots.*.finalized` - Finalized slots only
* `slots.{slot_number}.{status}` - Specific slot

### Transactions

{% code title="Go" %}

```go
// All transactions (high volume!)
txs := client.SubscribeAllTransactionsChannel(ctx)

// By program
txs := client.SubscribeProgramChannel(ctx, "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")

// By account
txs := client.SubscribeAccountTransactionsChannel(ctx, pubkey)
```

{% endcode %}

Topic patterns:

* `txs.slot.>` - All transactions
* `txs.program.{program_id}.>` - By program
* `txs.account.{pubkey}.>` - By account
* `txs.sig.{signature}.{slot}` - Specific transaction

### Entries

{% code title="Go" %}

```go
entries := client.SubscribeEntriesChannel(ctx)
```

{% endcode %}

Topic patterns:

* `entries.>` - All entries
* `entries.{slot}.{index}` - Specific entry

### Accounts

{% code title="Go" %}

```go
// All accounts (very high volume!)
accounts := client.SubscribeAccountsChannel(ctx)
```

{% endcode %}

Topic patterns:

* `accounts.>` - All account updates
* `accounts.pubkey.{pubkey}.{slot}` - Specific account
* `accounts.owner.{program}.{slot}` - By owner program

## SubscribeRequest (Yellowstone-Style)

`SubscribeRequest` enables server-side filtering, reducing bandwidth and latency. It's the recommended approach for production.

### Basic Usage

{% code title="Go" %}

```go
request := client.NewSubscribeRequest().
    AllSlots("my-slots").
    Transactions("my-txs", client.TransactionFilter{
        Vote: boolPtr(false),
    }).
    Entries("my-entries").
    Commitment(client.CommitmentConfirmed)

subscription, err := client.Subscribe(ctx, request)
```

{% endcode %}

### Filter Types

#### Slot Filter

{% code title="Go" %}

```go
request.Slots("slot-filter", client.SlotFilter{
    FilterByCommitment: true,  // Only at commitment level
})

// Shorthand for all slots
request.AllSlots("all-slots")
```

{% endcode %}

#### Transaction Filter

{% code title="Go" %}

```go
request.Transactions("tx-filter", client.TransactionFilter{
    Vote:            boolPtr(false),     // Exclude votes
    Failed:          boolPtr(false),     // Only successful
    Signature:       "5UGT...",          // Specific signature
    AccountInclude:  []string{"675k..."}, // Include if ANY match
    AccountExclude:  []string{"Vote..."}, // Exclude if ANY match
    AccountRequired: []string{"Toke..."}, // Require ALL
})
```

{% endcode %}

| Field           | Type      | Description                             |
| --------------- | --------- | --------------------------------------- |
| Vote            | \*bool    | true=votes only, false=non-votes only   |
| Failed          | \*bool    | true=failed only, false=successful only |
| Signature       | string    | Filter for specific signature           |
| AccountInclude  | \[]string | Include if ANY account present          |
| AccountExclude  | \[]string | Exclude if ANY account present          |
| AccountRequired | \[]string | Require ALL accounts present            |

#### Account Filter

{% code title="Go" %}

```go
request.Accounts("acc-filter", client.AccountFilter{
    Accounts: []string{"pubkey1", "pubkey2"}, // Specific accounts
    Owners:   []string{"TokenkegQf..."},      // By owner program
    Filters: []client.AccountDataFilter{
        {Datasize: uint64Ptr(165)},           // By data size
        {Memcmp: &client.MemcmpFilter{
            Offset: 0,
            Bytes:  []byte{1, 2, 3},
        }},
        {TokenAccountState: true},            // SPL tokens only
        {Lamports: &client.LamportsFilter{
            Eq: uint64Ptr(0),                 // Zero balance
        }},
    },
})
```

{% endcode %}

| Filter            | Description                   |
| ----------------- | ----------------------------- |
| Datasize          | Match exact account data size |
| Memcmp            | Match bytes at offset         |
| TokenAccountState | Only valid SPL token accounts |
| Lamports.Eq       | Exact lamport balance         |
| Lamports.Ne       | Not equal to balance          |
| Lamports.Lt       | Less than balance             |
| Lamports.Gt       | Greater than balance          |

#### Entry Filter

{% code title="Go" %}

```go
request.Entries("entry-filter")
```

{% endcode %}

Entries don't have filter options - you receive all entries or none.

#### Block Filter

{% code title="Go" %}

```go
request.Blocks("block-filter", client.BlockFilter{
    AccountInclude:      []string{},
    IncludeTransactions: true,
    IncludeAccounts:     false,
    IncludeEntries:      false,
})
```

{% endcode %}

#### Block Meta Filter

{% code title="Go" %}

```go
request.BlocksMeta("meta-filter")
```

{% endcode %}

Block metadata is lighter than full blocks - contains slot, blockhash, parent, rewards summary.

### Commitment Levels

{% code title="Go" %}

```go
request.Commitment(client.CommitmentProcessed)  // Fastest, may rollback
request.Commitment(client.CommitmentConfirmed)  // Balanced
request.Commitment(client.CommitmentFinalized)  // Slowest, irreversible
```

{% endcode %}

| Level     | Description                     | Use Case               |
| --------- | ------------------------------- | ---------------------- |
| Processed | Transaction processed by leader | Real-time display      |
| Confirmed | Confirmed by supermajority      | Trading, analytics     |
| Finalized | Guaranteed irreversible         | Settlement, accounting |

## Handling Updates

### Go Handler Pattern

{% code title="Go" %}

```go
err := subscription.Run(ctx, func(update *proto.SubscribeUpdate) error {
    switch u := update.UpdateOneof.(type) {
    case *proto.SubscribeUpdate_Slot:
        fmt.Printf("Slot: %d\n", u.Slot.Slot)
    case *proto.SubscribeUpdate_Transaction:
        fmt.Printf("TX: %x\n", u.Transaction.Transaction.Signature[:8])
    case *proto.SubscribeUpdate_Account:
        fmt.Printf("Account: %x\n", u.Account.Account.Pubkey[:8])
    case *proto.SubscribeUpdate_Entry:
        fmt.Printf("Entry: slot %d index %d\n", u.Entry.Slot, u.Entry.Index)
    case *proto.SubscribeUpdate_Block:
        fmt.Printf("Block: %d\n", u.Block.Slot)
    case *proto.SubscribeUpdate_Pong:
        // Keep-alive, ignore
    }
    return nil
})
```

{% endcode %}

### Rust Stream Pattern

{% code title="Rust" %}

```rust
use futures::StreamExt;
use thorpulse_client::proto::subscribe_update::UpdateOneof;

while let Some(result) = subscription.next().await {
    let update = result?;
    match update.update_oneof {
        Some(UpdateOneof::Slot(slot)) => {
            println!("Slot: {}", slot.slot);
        }
        Some(UpdateOneof::Transaction(tx)) => {
            println!("TX in slot: {}", tx.slot);
        }
        Some(UpdateOneof::Account(acc)) => {
            println!("Account in slot: {}", acc.slot);
        }
        Some(UpdateOneof::Entry(entry)) => {
            println!("Entry: {} / {}", entry.slot, entry.index);
        }
        Some(UpdateOneof::Pong(_)) => {
            // Keep-alive
        }
        _ => {}
    }
}
```

{% endcode %}

## Performance Considerations

### Subscription Limits

Each tier has subscription limits:

* Flash: 100 concurrent subscriptions
* Thor: 250 concurrent subscriptions
* Prime: Unlimited

### Topic Selection

Start specific, widen if needed:

{% stepper %}
{% step %}
**Specific account/program subscriptions**

Lowest bandwidth: subscribe to specific accounts or programs.
{% endstep %}

{% step %}
**SubscribeRequest with filters**

Server-side filtering reduces client bandwidth and latency.
{% endstep %}

{% step %}
**Simple topic subscriptions**

Client-side filtering on topic subscriptions; broader than filtered SubscribeRequest.
{% endstep %}

{% step %}
**Wildcard subscriptions**

Highest bandwidth: use only when you need broad coverage.
{% endstep %}
{% endstepper %}

### Buffer Sizing

For high-volume subscriptions:

{% code title="Go" %}

```go
// Go: Use buffered channels
slots := make(chan *proto.SubscribeUpdateSlot, 1000)
```

{% endcode %}

{% code title="Rust" %}

```rust
// Rust: Process quickly or buffer
let (tx, rx) = tokio::sync::mpsc::channel(1000);
```

{% endcode %}

### Backpressure

If you can't keep up:

{% stepper %}
{% step %}
**The NATS connection buffers messages**

This provides temporary buffering while you catch up.
{% endstep %}

{% step %}
**Eventually, slow consumers are disconnected**

If buffers overflow or consumers are too slow, NATS will disconnect them.
{% endstep %}

{% step %}
**Use client.Stats() to monitor buffer usage**

Monitor and react programmatically to growing buffers.
{% endstep %}
{% endstepper %}

## Common Patterns

### DEX Trading

{% code title="Go" %}

```go
request := client.NewSubscribeRequest().
    AllSlots("slots").
    Transactions("raydium", client.TransactionFilter{
        Vote:           boolPtr(false),
        Failed:         boolPtr(false),
        AccountInclude: []string{
            "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", // Raydium AMM
        },
    }).
    Commitment(client.CommitmentConfirmed)
```

{% endcode %}

### Token Monitoring

{% code title="Go" %}

```go
request := client.NewSubscribeRequest().
    Accounts("tokens", client.AccountFilter{
        Owners: []string{"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},
        Filters: []client.AccountDataFilter{
            {Datasize: uint64Ptr(165)}, // Token account size
        },
    }).
    Commitment(client.CommitmentConfirmed)
```

{% endcode %}

### Wallet Tracking

{% code title="Go" %}

```go
wallets := []string{"wallet1...", "wallet2...", "wallet3..."}

request := client.NewSubscribeRequest().
    Accounts("wallets", client.AccountFilter{
        Accounts: wallets,
    }).
    Transactions("wallet-txs", client.TransactionFilter{
        AccountInclude: wallets,
    })
```

{% endcode %}


# Topics

ThorPulse publishes data to NATS topics following a hierarchical naming convention. This document provides the complete topic schema.

## Topic Structure

Topics follow the pattern: `{type}.{identifiers}.{slot}`

NATS wildcards:

* `*` matches a single token (e.g., `slots.*.confirmed`)
* `>` matches one or more tokens (e.g., `slots.>`)

## Slot Topics

Slot status updates as the network processes blocks.

### Pattern

{% code title="Pattern" %}

```
slots.{slot}.{status}
```

{% endcode %}

### Statuses

| Status                 | Description                              |
| ---------------------- | ---------------------------------------- |
| `first_shred_received` | First shred for this slot was received   |
| `completed`            | All shreds received, slot is complete    |
| `processed`            | Slot has been processed by the validator |
| `confirmed`            | Slot has been confirmed by the cluster   |
| `finalized`            | Slot has been finalized (irreversible)   |
| `created_bank`         | Bank was created for this slot           |
| `dead`                 | Slot was marked as dead (fork)           |

### Examples

{% code title="Examples" %}

```
slots.234567890.first_shred_received
slots.234567890.completed
slots.234567890.processed
slots.234567890.confirmed
slots.234567890.finalized
```

{% endcode %}

### Subscription Patterns

| Pattern             | Description                   |
| ------------------- | ----------------------------- |
| `slots.>`           | All slot updates              |
| `slots.*.confirmed` | Only confirmed slots          |
| `slots.*.finalized` | Only finalized slots          |
| `slots.234567890.*` | All updates for specific slot |

***

## Transaction Topics

Transaction data published to multiple topics for flexible querying.

### By Signature

{% code title="Pattern" %}

```
txs.sig.{signature}.{slot}
```

{% endcode %}

Query a specific transaction by its base58 signature.

### By Slot

{% code title="Pattern" %}

```
txs.slot.{slot}
```

{% endcode %}

All transactions in a specific slot.

### By Program

{% code title="Pattern" %}

```
txs.program.{program_id}.{slot}
```

{% endcode %}

Transactions that executed a specific program.

### By Account

{% code title="Pattern" %}

```
txs.account.{pubkey}.{slot}
```

{% endcode %}

Transactions involving a specific account (any role).

### By Signer

{% code title="Pattern" %}

```
txs.signer.{pubkey}.{slot}
```

{% endcode %}

Transactions signed by a specific account.

### Examples

{% code title="Examples" %}

```
txs.sig.5KtP...abc.234567890
txs.slot.234567890
txs.program.TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.234567890
txs.account.So11111111111111111111111111111111111111112.234567890
txs.signer.9WzD...xyz.234567890
```

{% endcode %}

### Subscription Patterns

| Pattern                      | Description                         |
| ---------------------------- | ----------------------------------- |
| `txs.slot.>`                 | All transactions                    |
| `txs.program.TokenkegQf...>` | Token program transactions          |
| `txs.program.675kPX9MHT...>` | Raydium AMM transactions            |
| `txs.account.So111...>`      | Transactions involving wrapped SOL  |
| `txs.signer.9WzD...>`        | Transactions from a specific wallet |

***

## Account Topics

Account state updates when account data changes.

### By Pubkey

{% code title="Pattern" %}

```
accounts.pubkey.{pubkey}.{slot}
```

{% endcode %}

Updates to a specific account.

### By Owner

{% code title="Pattern" %}

```
accounts.owner.{owner_program}.{slot}
```

{% endcode %}

Updates to accounts owned by a specific program.

### Examples

{% code title="Examples" %}

```
accounts.pubkey.So11111111111111111111111111111111111111112.234567890
accounts.owner.TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.234567890
```

{% endcode %}

### Subscription Patterns

| Pattern                                             | Description              |
| --------------------------------------------------- | ------------------------ |
| `accounts.>`                                        | All account updates      |
| `accounts.pubkey.So111...>`                         | Specific account updates |
| `accounts.owner.TokenkegQf...>`                     | All token accounts       |
| `accounts.owner.11111111111111111111111111111111.>` | System program accounts  |

***

## Entry Topics

Ledger entry updates containing transaction hashes.

### Pattern

{% code title="Pattern" %}

```
entries.{slot}.{entry_index}
```

{% endcode %}

### Examples

{% code title="Examples" %}

```
entries.234567890.0
entries.234567890.1
entries.234567890.2
```

{% endcode %}

### Subscription Patterns

| Pattern               | Description           |
| --------------------- | --------------------- |
| `entries.>`           | All entries           |
| `entries.234567890.*` | All entries in a slot |

***

## Block Topics

Complete block data and metadata.

### Full Block

{% code title="Pattern" %}

```
blocks.{slot}
```

{% endcode %}

Complete block with all transactions, accounts, and entries.

### Block Metadata

{% code title="Pattern" %}

```
blocks.{slot}.meta
```

{% endcode %}

Block metadata only (hashes, counts, no transaction data).

### Examples

{% code title="Examples" %}

```
blocks.234567890
blocks.234567890.meta
```

{% endcode %}

### Subscription Patterns

| Pattern         | Description             |
| --------------- | ----------------------- |
| `blocks.>`      | All blocks and metadata |
| `blocks.*.meta` | Block metadata only     |

***

## Special Topics

### Subscribe Request

{% code title="Topic" %}

```
thorpulse.subscribe
```

{% endcode %}

Send a Yellowstone-style `SubscribeRequest` to this topic with a reply inbox.

### Unsubscribe

{% code title="Topic" %}

```
thorpulse.unsubscribe
```

{% endcode %}

Cancel an existing subscription.

***

## Topic Access by Tier

| Tier       | Allowed Topics                  |
| ---------- | ------------------------------- |
| Free       | `slots.>`                       |
| Pro        | `slots.>`, `txs.>`, `entries.>` |
| Enterprise | `>` (all topics)                |

***

## Common Program IDs

For convenience, here are common Solana program IDs:

| Program           | ID                                             |
| ----------------- | ---------------------------------------------- |
| System Program    | `11111111111111111111111111111111`             |
| Token Program     | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`  |
| Token-2022        | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`  |
| Associated Token  | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` |
| Memo Program      | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr`  |
| Raydium AMM       | `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` |
| Orca Whirlpool    | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc`  |
| Jupiter v6        | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4`  |
| Metaplex Metadata | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s`  |

***

## Message Format

All messages are serialized using Yellowstone-compatible protobuf format:

{% code title="SubscribeUpdate (protobuf)" %}

```protobuf
message SubscribeUpdate {
  repeated string filters = 1;
  oneof update_oneof {
    SubscribeUpdateAccount account = 2;
    SubscribeUpdateSlot slot = 3;
    SubscribeUpdateTransaction transaction = 4;
    SubscribeUpdateBlock block = 5;
    SubscribeUpdatePing ping = 6;
    SubscribeUpdatePong pong = 7;
    SubscribeUpdateBlockMeta block_meta = 8;
    SubscribeUpdateEntry entry = 9;
  }
  Timestamp created_at = 10;
}
```

{% endcode %}

See [Protobuf Reference](https://github.com/rpcpool/yellowstone-grpc/tree/master/yellowstone-grpc-proto/proto) for complete message definitions.


# ThorStreamer gRPC

High-performance Geyser gRPC service for real-time Solana blockchain data streaming.

## What is ThorStreamer?

ThorStreamer is a gRPC service; provides filtered, low-latency access to Solana on-chain events for trading bots, DeFi applications, and blockchain analytics. By filtering transactions at the source, we deliver only the data you need—making ThorStreamer one of the fastest Solana event streaming services available.


# Overview

## Key Features

* Real-time Transaction Streaming — Filter by 18+ DeFi programs (Raydium, Pump.fun, Meteora, Orca, etc.)
* Account State Monitoring — Track balance and data changes for specific accounts or program-owned accounts
* Wallet Transaction Tracking — Monitor transactions for up to 10 wallet addresses
* Slot Status Updates — Track block confirmations and network progression
* Multi-language SDKs — Official support for Go, Rust, and TypeScript

## Quick Links

<table data-view="cards"><thead><tr><th></th><th data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Quick Start</td><td></td></tr><tr><td>API Reference</td><td><a href="/pages/vnQTUOI6m1sbVGDPJNGY">/pages/vnQTUOI6m1sbVGDPJNGY</a></td></tr><tr><td>SDKs</td><td><a href="/pages/y2xterdO5IuBsaHJb3jM">/pages/y2xterdO5IuBsaHJb3jM</a></td></tr><tr><td>Error Handling</td><td></td></tr></tbody></table>

## Resources

* [GitHub Repository](https://github.com/thorlabsDev/ThorStreamer)
* <https://discord.gg/thorlabs>


# Architecture

ThorStreamer uses gRPC (HTTP/2) with Protocol Buffers for efficient, low-latency streaming. The service provides two interfaces:

### ThorStreamer Service (Unified Stream)

A single high-throughput stream delivering all event types through one connection.

{% code title="proto" %}

```protobuf
service ThorStreamer {
  rpc StreamUpdates(Empty) returns (stream MessageWrapper);
}
```

{% endcode %}

Use when: You need all events and have high-performance infrastructure (8+ cores, 16GB+ RAM).

### EventPublisher Service (Individual Streams)

Separate subscription methods for each event type with filtering options.

{% code title="proto" %}

```protobuf
service EventPublisher {
  rpc SubscribeToTransactions(Empty) returns (stream StreamResponse);
  rpc SubscribeToSlotStatus(Empty) returns (stream StreamResponse);
  rpc SubscribeToWalletTransactions(SubscribeWalletRequest) returns (stream StreamResponse);
  rpc SubscribeToAccountUpdates(SubscribeAccountsRequest) returns (stream StreamResponse);
}
```

{% endcode %}

Use when: You need specific event types or have modest hardware (2-4 cores, 2-4GB RAM).

## Authentication

All requests require a token via gRPC metadata:

{% code title="metadata" %}

```
metadata: {
  "authorization": "your_token"
}
```

{% endcode %}

Each token identifies a unique client and enforces subscription limits.

## Connection Details

| Property   | Value         |
| ---------- | ------------- |
| Protocol   | gRPC (HTTP/2) |
| Encryption | TLS enabled   |
| Port       | 50051         |

## Data Flow

```
┌─────────────┐     gRPC Stream      ┌──────────────┐
│   Client    │ ◄──────────────────► │ ThorStreamer │
│   (SDK)     │   MessageWrapper     │   Service    │
└─────────────┘   or StreamResponse  └──────────────┘
```

Events are delivered as they occur on-chain. The unified stream wraps all events in `MessageWrapper` with type identification. Individual streams return `StreamResponse` containing serialized event data.


# Quick Start

Get streaming Solana data in under 5 minutes.

## Prerequisites

* Authentication token (contact [ThorLabs Discord](https://discord.gg/thorlabs))
* Server address check [Node Locations and Access](/introduction/node-locations-and-access)

## Choose Your Language

{% tabs %}
{% tab title="Go" %}
**Installation**

{% code title="Install" %}

```bash
go get github.com/thorlabsDev/ThorStreamer/sdks/go@v0.1.0
```

{% endcode %}

**Stream Transactions**

{% code title="main.go" %}

```go
package main

import (
    "context"
    "log"
    "os"
    "time"

    "github.com/joho/godotenv"
    thorclient "github.com/thorlabsDev/ThorStreamer/sdks/go/client"
)

func main() {
    godotenv.Load()

    client, err := thorclient.NewClient(thorclient.Config{
        ServerAddr:     os.Getenv("SERVER_ADDRESS"),
        Token:          os.Getenv("AUTH_TOKEN"),
        DefaultTimeout: 30 * time.Second,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    stream, err := client.SubscribeToTransactions(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    for {
        msg, err := stream.Recv()
        if err != nil {
            break
        }
        if tx := msg.GetTransaction(); tx != nil {
            log.Printf("Transaction: slot=%d", tx.Transaction.Slot)
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Rust" %}
**Installation**

{% code title="Cargo.toml" %}

```toml
[dependencies]
thor-grpc-client = "0.1.0"
tokio = { version = "1", features = ["full"] }
```

{% endcode %}

**Stream Transactions**

{% code title="main.rs" %}

```rust
use thor_grpc_client::{ClientConfig, ThorClient, parse_message};
use thor_grpc_client::proto::thor_streamer::types::message_wrapper::EventMessage;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = ClientConfig {
        server_addr: std::env::var("SERVER_ADDRESS")?,
        token: std::env::var("AUTH_TOKEN")?,
        ..Default::default()
    };

    let mut client = ThorClient::new(config).await?;
    let mut stream = client.subscribe_to_transactions().await?;

    while let Some(response) = stream.message().await? {
        let msg = parse_message(&response.data)?;
        if let Some(EventMessage::Transaction(tx_wrapper)) = msg.event_message {
            if let Some(tx) = tx_wrapper.transaction {
                println!("Transaction: slot={}", tx.slot);
            }
        }
    }
    Ok(())
}
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
**Installation**

{% code title="Install" %}

```bash
npm install @grpc/grpc-js google-protobuf
```

{% endcode %}

**Stream Transactions**

{% code title="index.ts" %}

```typescript
import * as grpc from '@grpc/grpc-js';
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
import { EventPublisherClient } from './proto/publisher_grpc_pb';

const client = new EventPublisherClient(
    process.env.SERVER_ADDRESS!,
    grpc.credentials.createInsecure()
);

const metadata = new grpc.Metadata();
metadata.set('authorization', process.env.AUTH_TOKEN!);

const stream = client.subscribeToTransactions(new Empty(), metadata);

stream.on('data', (response) => {
    console.log('Transaction received:', response.getData().length, 'bytes');
});

stream.on('error', (err) => {
    console.error('Stream error:', err);
});
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
**Installation**

{% code title="Install" %}

```bash
pip install grpcio grpcio-tools
```

{% endcode %}

**Stream Transactions**

{% code title="stream.py" %}

```python
import grpc
import asyncio
from google.protobuf.empty_pb2 import Empty

# Generate proto files first:

# python -m grpc_tools.protoc -I./proto --python_out=. --grpc_python_out=. proto/*.proto

from proto import publisher_pb2_grpc

async def main():
    metadata = [('authorization', 'your-token')]

    async with grpc.aio.insecure_channel('your-server:50051') as channel:
        stub = publisher_pb2_grpc.EventPublisherStub(channel)
        stream = stub.SubscribeToTransactions(Empty(), metadata=metadata)

        async for response in stream:
            print(f"Transaction: {len(response.data)} bytes")

asyncio.run(main())
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Environment Setup

Create a `.env` file:

{% code title=".env" %}

```bash
SERVER_ADDRESS=your-server:50051
AUTH_TOKEN=your-auth-token
```

{% endcode %}

## Next Steps

* [API Reference](/infrastructure/thorstreamer-grpc/api-reference) — Explore all available methods
* [SDK Documentation](/infrastructure/thorstreamer-grpc/sdks) — Detailed guides for each language
* Error Handling — Handle errors gracefully


# Limits and Performance

## Subscription Limits

Each authentication token (client) is limited to:

| Limit                              | Value  |
| ---------------------------------- | ------ |
| **Total concurrent subscriptions** | 6      |
| Transaction streams                | 2 max  |
| Account update streams             | 5 max  |
| Slot status streams                | 2 max  |
| Wallet transaction streams         | 10 max |

### Per-Subscription Limits

| Subscription Type             | Limit |
| ----------------------------- | ----- |
| Wallet addresses per request  | 10    |
| Account addresses per request | 100   |

***

## Performance Requirements

### EventPublisher Service (Individual Streams)

Recommended for most use cases.

| Resource | Requirement        |
| -------- | ------------------ |
| CPU      | 2-4 cores          |
| RAM      | 2-4 GB             |
| Network  | Standard broadband |

### ThorStreamer Service (Unified Stream)

High-throughput stream combining all events. **Requires robust infrastructure.**

| Resource | Requirement                 |
| -------- | --------------------------- |
| CPU      | 8+ cores                    |
| RAM      | 16+ GB                      |
| Network  | Low-latency, high-bandwidth |

{% hint style="info" %}
Mandatory architecture:

* Worker pools for parallel message processing
* Asynchronous event handling
* Message queuing for backpressure management
  {% endhint %}

***

## Disconnection Policy

Clients are automatically disconnected when they cannot keep up with the stream:

* **High message drop rate** — >50% messages dropped consistently
* **Processing latency** — Sustained delays in message handling
* **Buffer overflow** — Client or server-side buffer saturation

### Warning Signs

Before disconnection, you may observe:

* Increasing latency in message delivery
* Gaps in slot numbers or transaction indices
* Server-side warning messages

### Mitigation

{% stepper %}
{% step %}
**Upgrade infrastructure**

Add more CPU, RAM, and improve network capacity to handle higher throughput and reduce latency.
{% endstep %}

{% step %}
**Optimize code**

Implement async processing and reduce per-message latency (for example: use goroutines, async tasks, or batching).
{% endstep %}

{% step %}
**Use individual streams**

Switch to EventPublisher streams which have lower throughput requirements if ThorStreamer is too demanding.
{% endstep %}

{% step %}
**Monitor resources**

Continuously track CPU, memory, and network utilization to detect and react to capacity issues early.
{% endstep %}
{% endstepper %}

***

## Choosing the Right Service

| Use Case                         | Recommended Service                             |
| -------------------------------- | ----------------------------------------------- |
| Monitor specific programs        | EventPublisher: `SubscribeToTransactions`       |
| Track wallet activity            | EventPublisher: `SubscribeToWalletTransactions` |
| Monitor account states           | EventPublisher: `SubscribeToAccountUpdates`     |
| Block confirmations              | EventPublisher: `SubscribeToSlotStatus`         |
| All data, high-performance infra | ThorStreamer: `StreamUpdates`                   |

***

## Optimization Tips

### Message Processing

{% code title="Go (example)" %}

```go
// Use goroutines for parallel processing (Go)
for {
    msg, err := stream.Recv()
    if err != nil {
        break
    }
    go processMessage(msg)  // Process concurrently
}
```

{% endcode %}

{% code title="Rust (example)" %}

```rust
// Use tokio::spawn for async processing (Rust)
while let Some(response) = stream.message().await? {
    let data = response.data.clone();
    tokio::spawn(async move {
        process_message(data).await;
    });
}
```

{% endcode %}

### Buffer Management

* Use bounded channels/queues to prevent memory exhaustion
* Implement circuit breakers for downstream systems
* Monitor queue depths as a health indicator

### Network

* Deploy clients in same region as ThorStreamer server
* Use persistent connections (gRPC handles this automatically)
* Avoid proxy layers that add latency


# API Reference

Complete reference for ThorStreamer gRPC services, message types, and supported DeFi program filters.

* **Protocol**: gRPC (HTTP/2) with Protocol Buffers
* **Authentication**: authorization: `<your_token>` in metadata


# Program Filters

Transaction streams are filtered to include only transactions interacting with these DeFi programs.

## Supported Programs

| Program                           | Program ID                                     |
| --------------------------------- | ---------------------------------------------- |
| **Pump.fun**                      | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P`  |
| **Pump.fun AMM**                  | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`  |
| **Pump.fun Fee Account**          | `CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM` |
| **Pump.fun: Raydium Migration**   | `39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg` |
| **Raydium Liquidity Pool V4**     | `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` |
| **Raydium CLMM**                  | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` |
| **Raydium CPMM**                  | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` |
| **Raydium Launchpad**             | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj`  |
| **Raydium Launchpad Authority**   | `WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh`  |
| **Meteora DLMM**                  | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo`  |
| **Meteora Pools**                 | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` |
| **Meteora DAMM v2**               | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG`  |
| **Meteora DBC: Pool Authority**   | `FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM` |
| **Meteora Dynamic Bonding Curve** | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN`  |
| **Orca (Whirlpool)**              | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc`  |
| **Lifinity V2**                   | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` |
| **OpenBook**                      | `srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX`  |
| **Fluxbeam**                      | `FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X` |
| **SolFi**                         | `SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe`  |
| **Vertigo**                       | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ`  |
| **Moonshot**                      | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG`  |
| **game.com**                      | `GameEs6zXFFGhE5zCdx2sqeRZkL7uYzPsZuSVn1fdxHF` |

## NFT Marketplaces

| Program           | Program ID                                    |
| ----------------- | --------------------------------------------- |
| **Magic Eden V2** | `M2mx93ekt1fmXSVkTrUL9xVFHkmME8HTUi5Cyc5aF7K` |
| **Tensor Swap**   | `TSWAPaqyCSx2KABk68Shruf4rp7CxcNi8hAsbdwmHbN` |
| **Tensor cNFT**   | `TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp` |

{% hint style="info" %}
Need a program that's not listed? Contact us on [Discord](https://discord.gg/thorlabs) to request additional program filters.
{% endhint %}


# Services

ThorStreamer provides two gRPC services for streaming Solana blockchain data.

{% hint style="info" %}
Recommended for most use cases: use the EventPublisher service for individual streams with lower throughput requirements. Use ThorStreamer service only if you need a high-throughput unified stream and can meet the infrastructure requirements.
{% endhint %}

## EventPublisher Service

Provides individual streams with lower throughput requirements.

### SubscribeToTransactions

Stream real-time transactions matching configured program filters.

{% code title="protobuf" %}

```protobuf
rpc SubscribeToTransactions(Empty) returns (stream StreamResponse)
```

{% endcode %}

Input: None\
Output: Stream of transactions from supported programs

Use cases:

* Monitor DeFi protocol interactions
* Track trading activity
* Market making strategies

***

### SubscribeToAccountUpdates

Stream account state changes with flexible filtering.

{% code title="protobuf" %}

```protobuf
rpc SubscribeToAccountUpdates(SubscribeAccountsRequest) returns (stream StreamResponse)
```

{% endcode %}

Input:

{% code title="protobuf" %}

```protobuf
message SubscribeAccountsRequest {
  repeated string account_address = 1;  // Specific accounts (max 100)
  repeated string owner_address = 2;    // Filter by owner program
}
```

{% endcode %}

Filtering options:

* Monitor specific accounts by address
* Monitor all accounts owned by specific programs
* Combine both filters

Use cases:

* Token balance tracking
* Program state monitoring
* Portfolio analytics

***

### SubscribeToSlotStatus

Monitor Solana slot progression and confirmations.

{% code title="protobuf" %}

```protobuf
rpc SubscribeToSlotStatus(Empty) returns (stream StreamResponse)
```

{% endcode %}

Input: None\
Output: Stream of slot status events

Use cases:

* Transaction confirmation tracking
* Block finality monitoring
* Network health checks

***

### SubscribeToWalletTransactions

Stream transactions involving specific wallet addresses.

{% code title="protobuf" %}

```protobuf
rpc SubscribeToWalletTransactions(SubscribeWalletRequest) returns (stream StreamResponse)
```

{% endcode %}

Input:

{% code title="protobuf" %}

```protobuf
message SubscribeWalletRequest {
  repeated string wallet_address = 1;  // Base58 addresses (max 10)
}
```

{% endcode %}

Use cases:

* Wallet activity monitoring
* User transaction tracking
* Portfolio analytics

***

## ThorStreamer Service

High-throughput unified stream combining all event types. This service requires robust infrastructure.

### StreamUpdates

Single stream delivering all events through `MessageWrapper`.

{% code title="protobuf" %}

```protobuf
rpc StreamUpdates(Empty) returns (stream MessageWrapper)
```

{% endcode %}

Output: Stream containing one of:

* `TransactionEventWrapper` — Transaction data with stream type
* `SubscribeUpdateAccountInfo` — Account state changes
* `SlotStatusEvent` — Slot progression

{% hint style="warning" %}
Requirements:

* 8+ CPU cores
* 16GB+ RAM
* Low-latency network
* Worker pools for parallel processing

Clients that cannot keep up will be automatically disconnected.
{% endhint %}


# Message Type

## Response Wrappers

### StreamResponse

Standard response for EventPublisher methods.

```protobuf
message StreamResponse {
  bytes data = 1;  // Serialized event data
}
```

### MessageWrapper

Unified wrapper for ThorStreamer service containing typed events.

```protobuf
message MessageWrapper {
  oneof event_message {
    SubscribeUpdateAccountInfo account_update = 1;
    SlotStatusEvent slot = 2;
    TransactionEventWrapper transaction = 3;
  }
}
```

***

## Transaction Events

### TransactionEventWrapper

```protobuf
message TransactionEventWrapper {
  StreamType stream_type = 1;
  TransactionEvent transaction = 2;
}

enum StreamType {
  STREAM_TYPE_UNSPECIFIED = 0;
  STREAM_TYPE_FILTERED = 1;    // From program filters
  STREAM_TYPE_WALLET = 2;      // From wallet subscription
  STREAM_TYPE_ACCOUNT = 3;     // From account subscription
}
```

### TransactionEvent

```protobuf
message TransactionEvent {
  uint64 slot = 1;                    // Slot number
  bytes signature = 2;                 // 64-byte signature
  uint64 index = 3;                   // Index within block
  bool is_vote = 4;                   // Vote transaction flag
  SanitizedTransaction transaction = 5;
  TransactionStatusMeta transaction_status_meta = 6;
}
```

### SanitizedTransaction

```protobuf
message SanitizedTransaction {
  Message message = 1;
  bytes message_hash = 2;              // 32 bytes
  repeated bytes signatures = 3;       // 64-byte signatures
  bool is_simple_vote_transaction = 4;
}
```

### TransactionStatusMeta

```protobuf
message TransactionStatusMeta {
  bool is_status_err = 1;              // Success/failure
  uint64 fee = 2;                      // Fee in lamports
  repeated uint64 pre_balances = 3;
  repeated uint64 post_balances = 4;
  repeated InnerInstructions inner_instructions = 5;
  repeated string log_messages = 6;
  repeated TransactionTokenBalance pre_token_balances = 7;
  repeated TransactionTokenBalance post_token_balances = 8;
  repeated Reward rewards = 9;
  string error_info = 10;
}
```

***

## Account Events

### SubscribeUpdateAccountInfo

```protobuf
message SubscribeUpdateAccountInfo {
  bytes pubkey = 1;              // Account public key
  uint64 lamports = 2;           // Balance in lamports
  bytes owner = 3;               // Owner program
  bool executable = 4;           // Executable flag
  uint64 rent_epoch = 5;
  bytes data = 6;                // Account data
  uint64 write_version = 7;
  optional bytes txn_signature = 8;
  optional SlotStatus slot = 9;
}
```

***

## Slot Events

### SlotStatusEvent

```protobuf
message SlotStatusEvent {
  uint64 slot = 1;
  uint64 parent = 2;
  int32 status = 3;              // 0=processed, 1=confirmed, 2=rooted
  bytes block_hash = 4;          // 32 bytes
  uint64 block_height = 5;
}
```

***

## Supporting Types

### Message (Transaction)

```protobuf
message Message {
  uint32 version = 1;                  // 0=legacy, 1=v0
  MessageHeader header = 2;
  bytes recent_block_hash = 3;         // 32 bytes
  repeated bytes account_keys = 4;     // 32-byte keys
  repeated CompiledInstruction instructions = 5;
  repeated MessageAddressTableLookup address_table_lookups = 6;  // v0 only
  LoadedAddresses loaded_addresses = 7;                          // v0 only
  repeated bool is_writable = 8;
}
```

### CompiledInstruction

```protobuf
message CompiledInstruction {
  uint32 program_id_index = 1;
  bytes data = 2;
  repeated uint32 accounts = 3;
}
```

### TransactionTokenBalance

```protobuf
message TransactionTokenBalance {
  uint32 account_index = 1;
  string mint = 2;
  UiTokenAmount ui_token_amount = 3;
  string owner = 4;
}

message UiTokenAmount {
  double ui_amount = 1;
  uint32 decimals = 2;
  string amount = 3;
  string ui_amount_string = 4;
}
```


# SDKs

Official client libraries for Go, Rust, and TypeScript. Each SDK provides type-safe access to all ThorStreamer services with built-in authentication handling.


# Go

Official Go client library for ThorStreamer.

## Installation

```bash
go get github.com/thorlabsDev/ThorStreamer/sdks/go@v0.1.0
```

## Requirements

* Go 1.23.2+

## Quick Start

{% code title="main.go" %}

```go
package main

import (
    "context"
    "log"
    "os"
    "time"

    "github.com/joho/godotenv"
    thorclient "github.com/thorlabsDev/ThorStreamer/sdks/go/client"
)

func main() {
    godotenv.Load()

    client, err := thorclient.NewClient(thorclient.Config{
        ServerAddr:     os.Getenv("SERVER_ADDRESS"),
        Token:          os.Getenv("AUTH_TOKEN"),
        DefaultTimeout: 30 * time.Second,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    stream, err := client.SubscribeToTransactions(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    for {
        msg, err := stream.Recv()
        if err != nil {
            if thorclient.IsStreamDone(err) {
                break
            }
            log.Printf("Error: %v", err)
            break
        }
        if tx := msg.GetTransaction(); tx != nil {
            log.Printf("Transaction: slot=%d", tx.Transaction.Slot)
        }
    }
}
```

{% endcode %}

## API Reference

### Creating a Client

```go
client, err := thorclient.NewClient(thorclient.Config{
    ServerAddr:     "server:50051",
    Token:          "your-token",
    DefaultTimeout: 30 * time.Second,
})
defer client.Close()
```

### SubscribeToTransactions

{% code title="subscribe\_transactions.go" %}

```go
stream, err := client.SubscribeToTransactions(ctx)

for {
    msg, err := stream.Recv()
    if err != nil {
        break
    }
    if tx := msg.GetTransaction(); tx != nil {
        log.Printf("Signature: %x", tx.Transaction.Signature)
    }
}
```

{% endcode %}

### SubscribeToSlotStatus

{% code title="subscribe\_slot\_status.go" %}

```go
stream, err := client.SubscribeToSlotStatus(ctx)

for {
    msg, err := stream.Recv()
    if err != nil {
        break
    }
    if slot := msg.GetSlot(); slot != nil {
        log.Printf("Slot: %d, Status: %d", slot.Slot, slot.Status)
    }
}
```

{% endcode %}

### SubscribeToWalletTransactions

Monitor up to 10 wallet addresses:

{% code title="subscribe\_wallets.go" %}

```go
wallets := []string{
    "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
    "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S",
}
stream, err := client.SubscribeToWalletTransactions(ctx, wallets)

for {
    msg, err := stream.Recv()
    if err != nil {
        break
    }
    if tx := msg.GetTransaction(); tx != nil {
        log.Printf("Wallet tx: slot=%d", tx.Transaction.Slot)
    }
}
```

{% endcode %}

### SubscribeToAccountUpdates

Monitor accounts with optional owner filtering:

{% code title="subscribe\_accounts.go" %}

```go
accounts := []string{"account1...", "account2..."}
owners := []string{"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}

stream, err := client.SubscribeToAccountUpdates(ctx, accounts, owners)

for {
    msg, err := stream.Recv()
    if err != nil {
        break
    }
    if acc := msg.GetAccountUpdate(); acc != nil {
        log.Printf("Account: %x, lamports=%d", acc.Pubkey, acc.Lamports)
    }
}
```

{% endcode %}

## Error Handling

```go
msg, err := stream.Recv()
if err != nil {
    if thorclient.IsStreamDone(err) {
        // Stream closed normally (EOF or context cancelled)
        return
    }
    // Handle other errors
    log.Printf("Stream error: %v", err)
    return
}
```

## Full Example

See [examples/golang-advanced](https://github.com/thorlabsDev/ThorStreamer/tree/master/examples/golang-advanced) for a complete implementation with filtering, logging, and event handling.

## Resources

* [pkg.go.dev Documentation](https://pkg.go.dev/github.com/thorlabsDev/ThorStreamer/sdks/go)
* [GitHub Repository](https://github.com/thorlabsDev/ThorStreamer)


# Rust

Official Rust client library for ThorStreamer with async/await support.

## Installation

Add to `Cargo.toml`:

{% code title="Cargo.toml" %}

```toml
[dependencies]
thorstreamer-grpc-client = "0.1"
tokio = { version = "1", features = ["full"] }
```

{% endcode %}

## Quick Start

{% code title="main.rs" %}

```rust
use thorstreamer_grpc_client::{ClientConfig, ThorClient, parse_message};
use thorstreamer_grpc_client::proto::thor_streamer::types::message_wrapper::EventMessage;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = ClientConfig {
        server_addr: "http://your-server:50051".to_string(),
        token: "your-token".to_string(),
        ..Default::default()
    };

    let mut client = ThorClient::new(config).await?;
    let mut stream = client.subscribe_to_transactions().await?;

    while let Some(response) = stream.message().await? {
        let msg = parse_message(&response.data)?;
        if let Some(EventMessage::Transaction(tx_wrapper)) = msg.event_message {
            if let Some(tx) = tx_wrapper.transaction {
                println!("Transaction: slot={}", tx.slot);
            }
        }
    }
    Ok(())
}
```

{% endcode %}

## API Reference

### Creating a Client

{% code title="create\_client.rs" %}

```rust
use thorstreamer_grpc_client::{ClientConfig, ThorClient};
use std::time::Duration;

let config = ClientConfig {
    server_addr: "http://server:50051".to_string(),
    token: "your-token".to_string(),
    timeout: Duration::from_secs(30),
};

let client = ThorClient::new(config).await?;
```

{% endcode %}

### subscribe\_to\_transactions

{% code title="subscribe\_to\_transactions.rs" %}

```rust
use thorstreamer_grpc_client::proto::thor_streamer::types::message_wrapper::EventMessage;

let mut stream = client.subscribe_to_transactions().await?;

while let Some(response) = stream.message().await? {
    let msg = parse_message(&response.data)?;
    if let Some(EventMessage::Transaction(tx_wrapper)) = msg.event_message {
        if let Some(tx) = tx_wrapper.transaction {
            let sig_hex: String = tx.signature.iter()
                .take(8)
                .map(|b| format!("{:02x}", b))
                .collect();
            println!("Transaction: slot={}, sig={}", tx.slot, sig_hex);
        }
    }
}
```

{% endcode %}

### subscribe\_to\_slot\_status

{% code title="subscribe\_to\_slot\_status.rs" %}

```rust
let mut stream = client.subscribe_to_slot_status().await?;

while let Some(response) = stream.message().await? {
    let msg = parse_message(&response.data)?;
    if let Some(EventMessage::Slot(slot)) = msg.event_message {
        println!("Slot: {}, status={}, height={}",
            slot.slot, slot.status, slot.block_height);
    }
}
```

{% endcode %}

### subscribe\_to\_wallet\_transactions

Monitor up to 10 wallet addresses:

{% code title="subscribe\_to\_wallet\_transactions.rs" %}

```rust
let wallets = vec![
    "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string(),
    "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S".to_string(),
];

let mut stream = client.subscribe_to_wallet_transactions(wallets).await?;

while let Some(response) = stream.message().await? {
    let msg = parse_message(&response.data)?;
    if let Some(EventMessage::Transaction(tx_wrapper)) = msg.event_message {
        println!("Wallet transaction received");
    }
}
```

{% endcode %}

### subscribe\_to\_account\_updates

Monitor accounts with optional owner filtering:

{% code title="subscribe\_to\_account\_updates.rs" %}

```rust
let accounts = vec!["account1...".to_string()];
let owners = vec!["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".to_string()];

let mut stream = client.subscribe_to_account_updates(accounts, owners).await?;

while let Some(response) = stream.message().await? {
    let msg = parse_message(&response.data)?;
    if let Some(EventMessage::AccountUpdate(update)) = msg.event_message {
        println!("Account: lamports={}", update.lamports);
    }
}
```

{% endcode %}

## Error Handling

{% code title="error\_handling.rs" %}

```rust
use tonic::Status;

match stream.message().await {
    Ok(Some(response)) => {
        // Process message
    }
    Ok(None) => {
        // Stream ended normally
        println!("Stream closed");
    }
    Err(status) => {
        // Handle gRPC error
        eprintln!("Stream error: {:?}", status);
    }
}
```

{% endcode %}

## Environment Variables

Using `dotenv`:

{% code title="Cargo.toml (dotenv)" %}

```toml
[dependencies]
dotenv = "0.15"
```

{% endcode %}

{% code title="dotenv\_usage.rs" %}

```rust
use dotenv::dotenv;

dotenv().ok();
let config = ClientConfig {
    server_addr: std::env::var("SERVER_ADDRESS")?,
    token: std::env::var("AUTH_TOKEN")?,
    ..Default::default()
};
```

{% endcode %}

## Full Example

See the complete implementation in the examples directory:\
<https://github.com/thorlabsDev/ThorStreamer/tree/master/examples/rust>

## Resources

* [thorstreamer-grpc-client](https://crates.io/crates/thorstreamer-grpc-client)
* [Documentation](https://docs.rs/thorstreamer-grpc-client)
* [GitHub Repository](https://github.com/thorlabsDev/ThorStreamer)


# Typescript

TypeScript/Node.js client for ThorStreamer using gRPC-js.

## Installation

{% code title="Install dependencies" %}

```bash
npm install @grpc/grpc-js google-protobuf
```

{% endcode %}

## Proto Generation

{% stepper %}
{% step %}
**Clone repository**

```bash
# Clone the repository
git clone https://github.com/thorlabsDev/ThorStreamer.git
cd ThorStreamer/examples/typescript
```

{% endstep %}

{% step %}
**Install dependencies**

```bash
# Install dependencies
npm install
```

{% endstep %}

{% step %}
**Generate proto files**

```bash
# Generate proto files
npm run proto:gen
npm run proto:ts
```

{% endstep %}
{% endstepper %}

## Quick Start

{% code title="Quick start example" %}

```typescript
import * as grpc from '@grpc/grpc-js';
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
import { EventPublisherClient } from './proto/publisher_grpc_pb';
import { StreamResponse } from './proto/publisher_pb';

const client = new EventPublisherClient(
    process.env.SERVER_ADDRESS!,
    grpc.credentials.createInsecure()
);

const metadata = new grpc.Metadata();
metadata.set('authorization', process.env.AUTH_TOKEN!);

const stream = client.subscribeToTransactions(new Empty(), metadata);

stream.on('data', (response: StreamResponse) => {
    console.log('Transaction:', response.getData().length, 'bytes');
});

stream.on('error', (err) => {
    console.error('Stream error:', err);
});

stream.on('end', () => {
    console.log('Stream ended');
});
```

{% endcode %}

## API Reference

### Creating a Client

{% code title="Create a client" %}

```typescript
import * as grpc from '@grpc/grpc-js';
import { EventPublisherClient } from './proto/publisher_grpc_pb';

const client = new EventPublisherClient(
    'your-server:50051',
    grpc.credentials.createInsecure()
);

const metadata = new grpc.Metadata();
metadata.set('authorization', 'your-token');
```

{% endcode %}

### subscribeToTransactions

{% code title="Subscribe to transactions" %}

```typescript
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';

const stream = client.subscribeToTransactions(new Empty(), metadata);

stream.on('data', (response: StreamResponse) => {
    const data = response.getData_asU8();
    // Deserialize transaction data
    console.log('Transaction received:', data.length, 'bytes');
});
```

{% endcode %}

### subscribeToSlotStatus

{% code title="Subscribe to slot status" %}

```typescript
const stream = client.subscribeToSlotStatus(new Empty(), metadata);

stream.on('data', (response: StreamResponse) => {
    const data = response.getData_asU8();
    // Deserialize slot status
    console.log('Slot update:', data.length, 'bytes');
});
```

{% endcode %}

### subscribeToWalletTransactions

Monitor up to 10 wallet addresses:

{% code title="Subscribe to wallet transactions" %}

```typescript
import { SubscribeWalletRequest } from './proto/publisher_pb';

const request = new SubscribeWalletRequest();
request.setWalletAddressList([
    '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
    '2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S',
]);

const stream = client.subscribeToWalletTransactions(request, metadata);

stream.on('data', (response: StreamResponse) => {
    console.log('Wallet transaction received');
});
```

{% endcode %}

### subscribeToAccountUpdates

Monitor accounts with optional owner filtering:

{% code title="Subscribe to account updates" %}

```typescript
import { SubscribeAccountsRequest } from './proto/publisher_pb';

const request = new SubscribeAccountsRequest();
request.setAccountAddressList(['account1...', 'account2...']);
request.setOwnerAddressList(['TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA']);

const stream = client.subscribeToAccountUpdates(request, metadata);

stream.on('data', (response: StreamResponse) => {
    console.log('Account update received');
});
```

{% endcode %}

## Error Handling

{% code title="Stream error handling" %}

```typescript
stream.on('error', (err: grpc.ServiceError) => {
    console.error('Error code:', err.code);
    console.error('Error message:', err.message);

    // Implement reconnection logic
    if (err.code === grpc.status.UNAVAILABLE) {
        // Server unavailable, retry with backoff
    }
});

stream.on('end', () => {
    console.log('Stream ended normally');
});
```

{% endcode %}

## Promise Wrapper

Wrap streams in Promises for async/await usage:

{% code title="Promise wrapper for stream" %}

```typescript
async function subscribeTransactions(): Promise<void> {
    return new Promise((resolve, reject) => {
        const stream = client.subscribeToTransactions(new Empty(), metadata);

        stream.on('data', (response: StreamResponse) => {
            processTransaction(response.getData_asU8());
        });

        stream.on('error', reject);
        stream.on('end', resolve);
    });
}

// Usage
try {
    await subscribeTransactions();
} catch (err) {
    console.error('Stream failed:', err);
}
```

{% endcode %}

## Full Example

See the complete implementation with proto generation scripts in the repository:

* <https://github.com/thorlabsDev/ThorStreamer/tree/master/examples/typescript>

## Build Scripts

Package.json scripts (descriptions shown in place of generator commands):

{% code title="package.json - scripts" %}

```json
{
  "scripts": {
    "proto:gen": "Generate JavaScript from protos",
    "proto:ts": "Generate TypeScript definitions",
    "proto:clean": "Remove generated files",
    "build": "tsc",
    "start": "node dist/client.js",
    "dev": "ts-node src/client.ts"
  }
}
```

{% endcode %}

## Resources

* [GitHub Repository](https://github.com/thorlabsDev/ThorStreamer)
* [gRPC for Node.js](https://www.npmjs.com/package/@grpc/grpc-js)


# Error Handling

## Error Categories

### Authentication Errors

| Error             | Description              | Action                         |
| ----------------- | ------------------------ | ------------------------------ |
| `UNAUTHENTICATED` | Token missing or invalid | Check token is set in metadata |
| `TOKEN_EXPIRED`   | Token has expired        | Obtain new token               |
| `INVALID_TOKEN`   | Token format incorrect   | Verify token format            |

### Subscription Limit Errors

| Error                                    | Description                   | Limit           |
| ---------------------------------------- | ----------------------------- | --------------- |
| `SUBSCRIPTION_LIMIT_REACHED`             | Total subscriptions exceeded  | 6 total         |
| `TRANSACTION_SUBSCRIPTION_LIMIT_REACHED` | Transaction streams exceeded  | 2 max           |
| `ACCOUNT_SUBSCRIPTION_LIMIT_REACHED`     | Account streams exceeded      | 5 max           |
| `SLOT_SUBSCRIPTION_LIMIT_REACHED`        | Slot streams exceeded         | 2 max           |
| `WALLET_SUBSCRIPTION_LIMIT_REACHED`      | Wallet streams exceeded       | 10 max          |
| `TOO_MANY_WALLET_ADDRESSES`              | Addresses in request exceeded | 10 per request  |
| `TOO_MANY_ACCOUNT_ADDRESSES`             | Accounts in request exceeded  | 100 per request |

### Connection Errors

| Error                | Description                    | Action                       |
| -------------------- | ------------------------------ | ---------------------------- |
| `CONNECTION_ERROR`   | Failed to establish connection | Check network/server address |
| `CONNECTION_CLOSED`  | Server closed connection       | Reconnect with backoff       |
| `CONNECTION_TIMEOUT` | Connection timed out           | Retry with backoff           |
| `STREAM_CLOSED`      | Stream was terminated          | Reconnect                    |

### Request Errors

| Error                     | Description                | Action                   |
| ------------------------- | -------------------------- | ------------------------ |
| `INVALID_REQUEST`         | Malformed request          | Check request format     |
| `INVALID_WALLET_ADDRESS`  | Bad wallet address format  | Use valid Base58 address |
| `INVALID_ACCOUNT_ADDRESS` | Bad account address format | Use valid Base58 address |
| `EMPTY_WALLET_LIST`       | No wallets provided        | Add at least one wallet  |
| `EMPTY_ACCOUNT_LIST`      | No accounts provided       | Add at least one account |

### Server Errors

| Error                | Description                | Action             |
| -------------------- | -------------------------- | ------------------ |
| `INTERNAL`           | Internal server error      | Contact support    |
| `UNAVAILABLE`        | Service unavailable        | Retry with backoff |
| `RESOURCE_EXHAUSTED` | Server resources exhausted | Retry later        |

## Recovery Strategies

### Reconnection with Exponential Backoff

{% code title="reconnect.go" %}

```go
// Go example
func reconnectWithBackoff(ctx context.Context, maxRetries int) error {
    backoff := time.Second
    for i := 0; i < maxRetries; i++ {
        stream, err := client.SubscribeToTransactions(ctx)
        if err == nil {
            return processStream(stream)
        }

        log.Printf("Connection failed, retrying in %v", backoff)
        time.Sleep(backoff)
        backoff *= 2
        if backoff > 30*time.Second {
            backoff = 30 * time.Second
        }
    }
    return errors.New("max retries exceeded")
}
```

{% endcode %}

{% code title="reconnect.rs" %}

```rust
// Rust example
async fn reconnect_with_backoff(max_retries: u32) -> Result<(), Box<dyn Error>> {
    let mut backoff = Duration::from_secs(1);

    for _ in 0..max_retries {
        match client.subscribe_to_transactions().await {
            Ok(stream) => return process_stream(stream).await,
            Err(e) => {
                println!("Connection failed, retrying in {:?}", backoff);
                tokio::time::sleep(backoff).await;
                backoff = std::cmp::min(backoff * 2, Duration::from_secs(30));
            }
        }
    }
    Err("max retries exceeded".into())
}
```

{% endcode %}

{% code title="reconnect.ts" %}

```typescript
// TypeScript example
async function reconnectWithBackoff(maxRetries: number): Promise<void> {
    let backoff = 1000;

    for (let i = 0; i < maxRetries; i++) {
        try {
            await subscribeToTransactions();
            return;
        } catch (err) {
            console.log(`Connection failed, retrying in ${backoff}ms`);
            await new Promise(r => setTimeout(r, backoff));
            backoff = Math.min(backoff * 2, 30000);
        }
    }
    throw new Error('max retries exceeded');
}
```

{% endcode %}

### Error Type Classification

{% code title="error\_classification.go" %}

```go
// Go
func handleError(err error) {
    switch {
    case thorclient.IsStreamDone(err):
        // Normal closure, reconnect
        reconnect()
    case isAuthError(err):
        // Authentication issue, refresh token
        refreshToken()
    case isRateLimitError(err):
        // Too many subscriptions, close unused
        closeUnusedSubscriptions()
    default:
        // Unknown error, log and retry
        log.Printf("Unknown error: %v", err)
        reconnectWithBackoff()
    }
}
```

{% endcode %}

## Monitoring Recommendations

{% stepper %}
{% step %}
**Track error frequencies**

Alert on unusual patterns.
{% endstep %}

{% step %}
**Monitor reconnection rates**

High rates indicate infrastructure issues.
{% endstep %}

{% step %}
**Log error contexts**

Include timestamps and request details.
{% endstep %}

{% step %}
**Set up health checks**

Verify stream connectivity periodically.
{% endstep %}
{% endstepper %}


# Thor ShredStream

> Raw Solana shreds streamed via UDP - the fastest on-chain signal available

## What is Thor ShredStream?

Thor ShredStream provides **raw Solana shreds via UDP**, giving you the earliest possible access to transaction data. Shreds arrive before blocks are confirmed, enabling sub-second reaction times for trading strategies.

### Key Benefits

| Feature             | Description                                                       |
| ------------------- | ----------------------------------------------------------------- |
| **Earliest Signal** | Receive shreds as they're produced, before block confirmation     |
| **Raw Data**        | Unprocessed shreds for custom parsing and strategy implementation |
| **Low Latency**     | Direct UDP delivery minimizes network overhead                    |
| **Reliable Source** | Enterprise-grade shred infrastructure                             |

## What Are Shreds?

Solana breaks transactions into small packets called **shreds** for fast network propagation. Each shred is \~1228 bytes containing:

* Slot number
* Shred index
* Shred type (data or coding/FEC)
* Transaction data fragment

Validators reassemble shreds into complete blocks. By receiving shreds directly, you see transaction data before the block is finalized.

## Use Cases

* **High-Frequency Trading (HFT)** - React to transactions before block confirmation
* **Arbitrage** - Detect price movements across DEXs at the earliest moment
* **MEV Strategies** - Build and submit transactions with minimal delay
* **Market Making** - Update quotes based on incoming order flow
* **Analytics** - Real-time network monitoring and statistics


# Technical Requirements

To use Shred Stream effectively, you need:

### Infrastructure

* **Public IP** with open UDP port (no NAT/CGNAT)
* **Low-latency server** close to Thor Labs RPC Nodes (recommended: Frankfurt)
* **Sufficient bandwidth** (\~50-100 Mbps for full shred stream)

### Development Capability

* **Deshredding logic** to reconstruct transactions from shred fragments
* **FEC decoding** (optional) to recover from packet loss
* **Custom parsing** for your specific use case

### Example Client (Go)

{% code title="shred\_listener.go" %}

```go
package main

import (
    "encoding/binary"
    "fmt"
    "net"
)

// Solana shred header offsets (per spec)
const (
    OffsetVariant = 64 // 0x40: Shred variant (1 byte)
    OffsetSlot    = 65 // 0x41: Slot number (8 bytes, u64 LE)
    OffsetIndex   = 73 // 0x49: Shred index (4 bytes, u32 LE)
)

func main() {
    addr, _ := net.ResolveUDPAddr("udp", ":9000")
    conn, _ := net.ListenUDP("udp", addr)
    defer conn.Close()

    buf := make([]byte, 1232)

    for {
        n, _, err := conn.ReadFromUDP(buf)
        if err != nil {
            continue
        }

        shred := buf[:n]
        variant := shred[OffsetVariant]
        slot := binary.LittleEndian.Uint64(shred[OffsetSlot : OffsetSlot+8])
        index := binary.LittleEndian.Uint32(shred[OffsetIndex : OffsetIndex+4])

        fmt.Printf("slot=%d index=%d variant=0x%02x size=%d\n",
            slot, index, variant, n)
    }
}
```

{% endcode %}

{% hint style="info" %}
This example listens on UDP port 9000 and parses the variant, slot, and index fields from each received shred according to the Solana shred header offsets.
{% endhint %}

### Shred Format

Per the [Solana shred specification](https://github.com/solana-foundation/specs/blob/main/p2p/shred.md):

| Offset | Size   | Field                                |
| ------ | ------ | ------------------------------------ |
| 0x00   | 64     | Signature (Ed25519)                  |
| 0x40   | 1      | Variant (encodes type + auth method) |
| 0x41   | 8      | Slot (u64 little endian)             |
| 0x49   | 4      | Index (u32 little endian)            |
| 0x4D   | 2      | Version (u16 little endian)          |
| 0x4F   | 4      | FEC Set Index (u32 little endian)    |
| 0x53+  | varies | Type-specific header + payload       |

The **variant** byte encodes both the shred type (data/coding) and authentication method (legacy/merkle) as two 4-bit values.

{% hint style="warning" %}
Maximum shred size: 1228 bytes (fits in a single UDP packet). Ensure your UDP buffer and networking configuration can handle this size and the expected throughput (\~50–100 Mbps).
{% endhint %}


# Getting Started

{% stepper %}
{% step %}
**Request Access**

Get your access with the Dashboard bot in the [Thor Labs Discord Server](https://discord.gg/thorlabs). You'll need to provide:

* Server IP address
* Preferred UDP port
  {% endstep %}

{% step %}
**Receive Credentials**

Once the bot approves, you'll receive confirmation that your IP:port is registered.
{% endstep %}

{% step %}
**Start Receiving**

Run your UDP listener on the registered port. Shreds will begin flowing immediately.
{% endstep %}

{% step %}
**Monitor Health**

Check that you're receiving shreds:

{% code title="Monitor packets (example)" %}

```bash
# Count packets per second
tcpdump -i eth0 udp port 9000 2>/dev/null | pv -l -i 5 > /dev/null
```

{% endcode %}

Expected: 3,000-10,000 shreds/second depending on network activity.
{% endstep %}
{% endstepper %}

## Data Characteristics

| Metric            | Typical Value                                   |
| ----------------- | ----------------------------------------------- |
| Shreds per second | 3,000 - 10,000                                  |
| Shred size        | 800 - 1,228 bytes                               |
| Bandwidth         | 30 - 100 Mbps                                   |
| Latency advantage | <p>200 - 400ms vs RPC<br>100-200 ms vs gRPC</p> |

## Next Steps

* [Choose Your Data Source](/infrastructure/thor-shredstream/choose-your-data-source) — Explore all available data sources
* FAQs — Frequently Asked Questions


# FAQs

## Frequently Asked Questions

<details>

<summary>Do I need to handle packet loss?</summary>

Yes, UDP does not guarantee delivery. For critical applications, implement FEC (Forward Error Correction) decoding using coding shreds, or accept occasional gaps.

</details>

<details>

<summary>Can I receive shreds behind NAT?</summary>

No. You need a public IP with a directly accessible UDP port. Home connections with CGNAT will not work.

</details>

<details>

<summary>What's the difference between data and coding shreds?</summary>

* **Data shreds** (type=0) contain actual transaction data
* **Coding shreds** (type=1) are for FEC - used to recover lost data shreds

</details>

<details>

<summary>How do I reconstruct transactions?</summary>

Collect all data shreds for a slot, sort by index, concatenate payloads, and parse the resulting entries. This requires understanding Solana's entry and transaction formats.

</details>

<details>

<summary>What locations are available?</summary>

Shred sources deployed in regions with highest validator and stake density. Contact us for availability.

</details>

## Support

For access requests and technical support, contact [Thor Labs](https://discord.gg/thor_labs).


# Choose Your Data Source

Choosing the right data source depends on your latency requirements, technical capabilities, and use case. This guide compares the three main options for receiving Solana transaction data.

## Quick Comparison

| Aspect              | Shred Stream        | Geyser gRPC                  | RPC                 |
| ------------------- | ------------------- | ---------------------------- | ------------------- |
| **Latency**         | Fastest             | Fast                         | Standard            |
| **Timing**          | Before confirmation | After slot processing        | After confirmation  |
| **Data format**     | Raw shreds          | Parsed transactions/accounts | Parsed transactions |
| **Protocol**        | UDP                 | TCP/gRPC                     | HTTP/WebSocket      |
| **Delivery**        | Best-effort         | Guaranteed                   | Guaranteed          |
| **Complexity**      | High                | Medium                       | Low                 |
| **Packet loss**     | Possible            | None                         | None                |
| **Historical data** | No                  | Limited                      | Yes                 |
| **Setup effort**    | Significant         | Moderate                     | Minimal             |

## Latency Breakdown

{% stepper %}
{% step %}
**SHRED STREAM (\~0ms)**

Raw shreds available immediately when produced by the block leader.
{% endstep %}

{% step %}
**GEYSER gRPC (\~100-200ms)**

Slot processed, transactions parsed.
{% endstep %}

{% step %}
**RPC (\~400-800ms)**

Block confirmed, available via API.
{% endstep %}
{% endstepper %}

## Decision Matrix

| Your requirement          | Best choice            |
| ------------------------- | ---------------------- |
| Absolute lowest latency   | ShredStream            |
| Latency-sensitive trading | Shred Stream or Geyser |
| Real-time parsed data     | Geyser gRPC            |
| Simple integration        | RPC                    |
| Guaranteed delivery       | Geyser or RPC          |
| Historical data           | RPC                    |
| Account state streaming   | Geyser gRPC            |
| MEV/HFT strategies        | Shred Stream           |
| Building an indexer       | Geyser gRPC            |
| Wallet/explorer           | RPC                    |

## Technical Requirements by Option

### ShredStream

* Public IP with open UDP port
* Low-latency server (Frankfurt, NYC, Tokyo recommended)
* 50-100 Mbps bandwidth
* Custom deshredding implementation
* FEC decoding (optional, for packet loss recovery)

### Geyser gRPC

* gRPC client library
* Stable TCP connection
* Protobuf parsing
* Subscription management logic

### RPC

* HTTP client
* JSON parsing
* Basic error handling

## Cost Considerations

| Option      | Infrastructure          | Development           | Operational                |
| ----------- | ----------------------- | --------------------- | -------------------------- |
| ShredStream | High (dedicated server) | High (custom parsing) | Medium                     |
| Geyser gRPC | Medium                  | Medium                | Medium                     |
| RPC         | Low                     | Low                   | Low (may have rate limits) |

{% hint style="info" %}
Summary:

* ShredStream is for teams who need every millisecond of advantage and have the technical capability to handle raw shreds. It's the fastest but most complex option.
* Geyser gRPC is the sweet spot for most real-time applications. You get low latency with parsed data and reliable delivery.
* RPC is the standard choice for applications where latency isn't critical. It's simple, reliable, and widely supported.

Choose based on your latency requirements and technical capabilities. When in doubt, start with Geyser or RPC and move to ShredStream only if you need the extra edge.
{% endhint %}


# Shred Stream

### What it is

Raw Solana shreds delivered via UDP as they're produced by the block leader. Shreds are the smallest unit of data in Solana's Turbine protocol.

### Latency

\~0ms from production — You receive shreds at the same time validators do, before any processing or confirmation.

### Data format

Raw binary shreds (1228 bytes max) containing:

* Slot number
* Shred index
* Shred type (data/coding)
* Transaction data fragments

### Pros

* Absolute lowest latency available
* See transactions before anyone using Geyser or RPC
* Direct network-level access

### Cons

* Requires custom deshredding logic
* UDP may lose packets (no retransmission)
* High technical complexity
* No historical replay
* Requires public IP with open UDP port

### Best for

* High-frequency trading (HFT)
* MEV searchers
* Arbitrage bots
* Latency-critical applications

### Example use case

A MEV bot detects a large swap in shreds, builds a backrun transaction, and submits it before the original transaction is even confirmed.


# Geyser gRPC (ThorStreamer, Yellowstone)

### What it is

Streaming API that delivers parsed transactions and account updates via gRPC after slots are processed by a validator.

### Latency

\~100-200ms — Data is available after the validator processes the slot but before network-wide confirmation.

### Data format

Structured protobuf messages containing:

* Parsed transactions with instructions
* Account updates with before/after states
* Slot notifications
* Block metadata

### Pros

* Parsed, ready-to-use data
* Reliable TCP delivery (no packet loss)
* Filter by program, account, or transaction type
* Easier to integrate than shreds

### Cons

* Slower than ShredStream
* Requires gRPC client implementation
* Limited historical replay
* Still requires dedicated infrastructure

### Best for

* Trading bots
* Real-time indexers
* DeFi applications
* Account monitoring

### Example use case

A trading bot subscribes to a specific DEX program, receives parsed swap instructions, and updates internal price feeds in real-time.


# RPC (getBlock, WebSocket)

### What it is

Standard Solana JSON-RPC API for querying confirmed blocks and subscribing to updates via WebSocket.

### Latency

\~400-800ms+ — Data is available after block confirmation and propagation across the network.

### Data format

JSON responses containing:

* Fully confirmed blocks
* Parsed transactions
* Account states
* Network status

### Pros

* Simple HTTP/WebSocket integration
* Guaranteed data consistency
* Historical data access
* Wide library support
* No special infrastructure needed

### Cons

* Highest latency of the three options
* Not suitable for latency-sensitive strategies
* Rate limits may apply

### Best for

* General applications
* Wallets and explorers
* Analytics and reporting
* Applications where correctness matters more than speed

### Example use case

A portfolio tracker queries confirmed transactions to display user balances and transaction history.


# Technical Requirements by Option

### ShredStream

* Public IP with open UDP port
* Low-latency server (Frankfurt recommended)
* 50-100 Mbps bandwidth
* Custom deshredding implementation
* FEC decoding (optional, for packet loss recovery)

### Geyser gRPC

* gRPC client library
* Stable TCP connection
* Protobuf parsing
* Subscription management logic

### RPC

* HTTP client
* JSON parsing
* Basic error handling

## Cost Considerations

| Option      | Infrastructure | Development           | Operational                |
| ----------- | -------------- | --------------------- | -------------------------- |
| ShredStream | High           | High (custom parsing) | Medium                     |
| Geyser gRPC | Medium         | Medium                | Medium                     |
| RPC         | Low            | Low                   | Low (may have rate limits) |

{% hint style="info" %}
Summary:

* ShredStream is for teams who need every millisecond of advantage and have the technical capability to handle raw shreds. It's the fastest but most complex option.
* Geyser gRPC is the sweet spot for most real-time applications. You get low latency with parsed data and reliable delivery.
* RPC is the standard choice for applications where latency isn't critical. It's simple, reliable, and widely supported.

Choose based on your latency requirements and technical capabilities. When in doubt, start with gRPC or RPC and move to Shred Stream only if you need the extra edge.
{% endhint %}


# FastGate RPC

FastGate RPC is a high-performance Solana RPC endpoint optimized for account queries. It provides low-latency responses for `getProgramAccounts` and related methods.

## Supported Methods

| Method                         | Description                           |
| ------------------------------ | ------------------------------------- |
| `getProgramAccounts`           | Get all accounts owned by a program   |
| `getProgramAccountsV2`         | Paginated version with cursor support |
| `getMultipleAccounts`          | Get multiple accounts by pubkey       |
| `getTokenAccountsByOwner`      | Get SPL token accounts for a wallet   |
| `getTokenAccountsByOwnerV2`    | Paginated version with cursor support |
| `getTokenAccountsByDelegate`   | Get delegated SPL token accounts      |
| `getTokenAccountsByDelegateV2` | Paginated version with cursor support |
| `getTokenLargestAccounts`      | Get top 20 token holders for a mint   |
| `getLargestAccounts`           | Get top 20 accounts by lamports       |

## Endpoint

```
POST https://rpc.thornode.io/
Content-Type: application/json
```

## Authentication

Requests require IP allowlist authentication. See the Authentication section.

## Encoding

| Encoding      | Description                                        |
| ------------- | -------------------------------------------------- |
| `base64`      | Default. Standard base64                           |
| `base58`      | Base58 encoding                                    |
| `base64+zstd` | Zstd-compressed, base64-encoded (smallest payload) |

## REST Endpoint

A REST interface is also available:

```
GET /getProgramAccounts/{programId}?encoding=base64&limit=100&after=<pubkey>
```


# Introduction

FastGate RPC is a dedicated Solana RPC endpoint purpose-built for high-speed account queries. It delivers consistently low latency across all `getProgramAccounts` variants and token account methods — with full Solana RPC compatibility.

## Why FastGate

* **Sub-200ms response times** on paginated queries across all methods
* **100% uptime** on all 9 supported methods — no blocked or unsupported calls
* **Full Solana RPC compatibility** — drop-in replacement for account query methods
* **Pagination support** — cursor-based pagination with `getProgramAccountsV2`
* **Incremental sync** — `changedSinceSlot` parameter for delta updates
* **Multiple encodings** — `base64`, `base58`, and `base64+zstd` for compressed payloads

## Performance

All benchmarks measured against Helius RPC under identical conditions (same methods, same parameters, same network path).

### Low-Stress Benchmark (10 req/method, 9 methods)

| Metric                | FastGate     | Helius      |
| --------------------- | ------------ | ----------- |
| **Success Rate**      | 90/90 (100%) | 70/90 (78%) |
| **p50 Latency**       | 179 ms       | 413 ms      |
| **p95 Latency**       | 951 ms       | 1,631 ms    |
| **Timeouts**          | 0            | 10          |
| **Methods Supported** | 9/9          | 7/9         |

### Per-Method Latency (p50)

| Method                             | FastGate | Helius   | Speedup  |
| ---------------------------------- | -------- | -------- | -------- |
| getProgramAccountsV2 (unfiltered)  | 179 ms   | 419 ms   | **2.3x** |
| getProgramAccountsV2 (filtered)    | 207 ms   | 420 ms   | **2.0x** |
| getProgramAccounts V1 (unfiltered) | 928 ms   | 1,574 ms | **1.7x** |
| getProgramAccounts V1 (filtered)   | 932 ms   | 1,541 ms | **1.7x** |
| getMultipleAccounts                | 175 ms   | 252 ms   | **1.4x** |
| getTokenAccountsByOwner            | 181 ms   | 252 ms   | **1.4x** |
| getTokenLargestAccounts            | 175 ms   | 263 ms   | **1.5x** |
| getTokenAccountsByDelegate         | 176 ms   | timeout  | n/a      |
| getLargestAccounts                 | 176 ms   | blocked  | n/a      |

### High-Load Benchmark (50 RPS sustained, 30 seconds)

| Metric          | FastGate      | Helius        |
| --------------- | ------------- | ------------- |
| **Completed**   | 1,501 / 1,501 | 1,500 / 1,500 |
| **Errors**      | 0 (0%)        | 596 (40%)     |
| **p50 Latency** | 262 ms        | 5,000 ms      |
| **p95 Latency** | 459 ms        | 7,521 ms      |
| **p99 Latency** | 538 ms        | 9,445 ms      |

Under sustained load, FastGate maintains sub-second latencies. Helius applies stricter rate limits at this volume, which results in higher timeout rates — expected behavior for a shared public RPC tier.

## Quick Start

Send a JSON-RPC request to the endpoint:

```bash
curl -X POST https://rpc.thornode.io/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getProgramAccountsV2",
    "params": [
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
      {
        "encoding": "base64",
        "filters": [{ "dataSize": 165 }],
        "limit": 200
      }
    ]
  }'
```

See the individual method pages for detailed parameter documentation and response formats.


# getProgramAccounts

Returns all accounts owned by the specified program.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getProgramAccounts",
  "params": [
    "<programId>",
    {
      "encoding": "base64",
      "filters": [],
      "dataSlice": { "offset": 0, "length": 100 },
      "withContext": false
    }
  ]
}
```

## Parameters

| Parameter     | Type    | Required | Description                                                        |
| ------------- | ------- | -------- | ------------------------------------------------------------------ |
| `programId`   | string  | Yes      | Base58-encoded program public key                                  |
| `encoding`    | string  | No       | `"base64"` (default), `"base58"`, or `"base64+zstd"`               |
| `filters`     | array   | No       | Filter results using `dataSize` or `memcmp`                        |
| `dataSlice`   | object  | No       | Limit returned data to `{ offset, length }`                        |
| `withContext` | bool    | No       | If `true`, wraps result in `{ context: { slot }, value: [...] }`   |
| `limit`       | integer | No       | Max results per page (1–10,000, default 1,000). Enables pagination |
| `after`       | string  | No       | Cursor pubkey for pagination. Returns results after this key       |

## Filters

### dataSize

```json
{ "dataSize": 165 }
```

Only return accounts with the specified data length in bytes.

### memcmp

```json
{
  "memcmp": {
    "offset": 32,
    "bytes": "<base58-encoded-bytes>",
    "encoding": "base58"
  }
}
```

| Field      | Type    | Description                        |
| ---------- | ------- | ---------------------------------- |
| `offset`   | integer | Byte offset into account data      |
| `bytes`    | string  | Encoded bytes to match             |
| `encoding` | string  | `"base58"` (default) or `"base64"` |

## Response (Standard)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "pubkey": "AccountPublicKey...",
      "account": {
        "data": ["base64-data...", "base64"],
        "executable": false,
        "lamports": 2039280,
        "owner": "ProgramPublicKey...",
        "rentEpoch": 361,
        "space": 165
      }
    }
  ]
}
```

## Response (with `withContext: true`)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "pubkey": "AccountPublicKey...",
        "account": { ... }
      }
    ]
  }
}
```

## Response (Paginated — when `limit` or `after` is provided)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "accounts": [ ... ],
    "paginationKey": "LastAccountPubkey...",
    "totalResults": 54321
  }
}
```

| Field           | Type           | Description                                         |
| --------------- | -------------- | --------------------------------------------------- |
| `accounts`      | array          | Array of account objects                            |
| `paginationKey` | string or null | Pass as `after` in next request. `null` = last page |
| `totalResults`  | integer        | Total matching accounts across all pages            |

## Example

```bash
curl -X POST https://rpc.thornode.io/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getProgramAccounts",
    "params": [
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
      {
        "encoding": "base64",
        "filters": [
          { "dataSize": 165 },
          { "memcmp": { "offset": 32, "bytes": "YourWalletPubkey..." } }
        ]
      }
    ]
  }'
```

## Large Query Protection

Queries that would return more than **100,000 accounts** (or **10,000** for Token Program / Token-2022) are automatically rejected with a `-32600` error directing you to use `getProgramAccountsV2` with pagination instead.

This applies to unfiltered or broadly-filtered V1 requests. To avoid this, either:

* Use `getProgramAccountsV2` with `limit` and `paginationKey` for large result sets
* Add `dataSize` or `memcmp` filters to narrow results below the threshold

**Example error response:**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32600,
    "message": "Too many accounts requested (Large number of pubkeys), Please use getProgramAccountsV2 with pagination to handle large datasets."
  }
}
```

## Error Codes

| Code     | Message                     | Description                                         |
| -------- | --------------------------- | --------------------------------------------------- |
| `-32600` | Too many accounts requested | Result set too large for V1. Use V2 with pagination |
| `-32602` | Invalid params              | Missing or malformed parameters                     |
| `-32004` | Program is still indexing   | Data not yet available, retry shortly               |
| `-32603` | Internal error              | Server-side error                                   |


# getProgramAccountsV2

Paginated version of `getProgramAccounts` with cursor-based navigation and total count.

> **Note:** Large V1 `getProgramAccounts` queries are automatically rejected and redirected here. If you received a `-32600` error from V1, switch to this method with `limit` and `paginationKey` parameters.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getProgramAccountsV2",
  "params": [
    "<programId>",
    {
      "encoding": "base64",
      "filters": [],
      "limit": 200,
      "paginationKey": null,
      "changedSinceSlot": null
    }
  ]
}
```

## Parameters

| Parameter          | Type    | Required | Description                                          |
| ------------------ | ------- | -------- | ---------------------------------------------------- |
| `programId`        | string  | Yes      | Base58-encoded program public key                    |
| `encoding`         | string  | No       | `"base64"` (default), `"base58"`, or `"base64+zstd"` |
| `filters`          | array   | No       | Same as `getProgramAccounts` (`dataSize`, `memcmp`)  |
| `dataSlice`        | object  | No       | `{ offset, length }` — limit returned data bytes     |
| `limit`            | integer | No       | Page size, 1–10,000 (default 1,000)                  |
| `paginationKey`    | string  | No       | Cursor from previous response to fetch the next page |
| `changedSinceSlot` | integer | No       | Only return accounts updated after this slot         |

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "accounts": [
      {
        "pubkey": "AccountPubkey...",
        "account": {
          "data": ["base64-data...", "base64"],
          "executable": false,
          "lamports": 2039280,
          "owner": "ProgramPubkey...",
          "rentEpoch": 361,
          "space": 165
        }
      }
    ],
    "paginationKey": "LastPubkeyInPage...",
    "totalResults": 1423567
  }
}
```

| Field           | Type           | Description                                              |
| --------------- | -------------- | -------------------------------------------------------- |
| `accounts`      | array          | Account objects for this page                            |
| `paginationKey` | string or null | Cursor for next page. `null` means this is the last page |
| `totalResults`  | integer        | Total number of matching accounts                        |

## Pagination Example

**Page 1:**

```json
{
  "method": "getProgramAccountsV2",
  "params": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", { "limit": 200 }]
}
```

**Page 2 (using paginationKey from page 1):**

```json
{
  "method": "getProgramAccountsV2",
  "params": [
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    { "limit": 200, "paginationKey": "LastPubkeyFromPage1..." }
  ]
}
```

## changedSinceSlot

Use `changedSinceSlot` to fetch only accounts that have been updated after a specific slot. This is useful for incremental syncing.

```json
{
  "method": "getProgramAccountsV2",
  "params": [
    "ProgramId...",
    { "changedSinceSlot": 280000000, "limit": 1000 }
  ]
}
```


# getMultipleAccounts

Returns account information for a list of public keys. Missing accounts are returned as `null`.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getMultipleAccounts",
  "params": [
    ["Pubkey1...", "Pubkey2...", "Pubkey3..."],
    {
      "encoding": "base64",
      "dataSlice": { "offset": 0, "length": 128 }
    }
  ]
}
```

## Parameters

| Parameter   | Type      | Required | Description                                          |
| ----------- | --------- | -------- | ---------------------------------------------------- |
| pubkeys     | string\[] | Yes      | Array of base58-encoded public keys                  |
| `encoding`  | string    | No       | `"base64"` (default), `"base58"`, or `"base64+zstd"` |
| `dataSlice` | object    | No       | `{ offset, length }` — limit returned data bytes     |

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "data": ["base64-data...", "base64"],
        "executable": false,
        "lamports": 2039280,
        "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
        "rentEpoch": 361,
        "space": 165
      },
      null,
      {
        "data": ["base64-data...", "base64"],
        "executable": false,
        "lamports": 5000000,
        "owner": "11111111111111111111111111111111",
        "rentEpoch": 361,
        "space": 0
      }
    ]
  }
}
```

Accounts are returned in the same order as the input array. If an account does not exist, `null` is returned at that position.

## Example

```bash
curl -X POST https://rpc.thornode.io/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getMultipleAccounts",
    "params": [
      [
        "So11111111111111111111111111111111111111112",
        "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      ],
      { "encoding": "base64" }
    ]
  }'
```


# getTokenAccountsByOwner

Returns all SPL Token accounts owned by a wallet. Searches both the Token Program and Token-2022 Program.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenAccountsByOwner",
  "params": [
    "<walletPubkey>",
    { "mint": "<mintPubkey>" },
    { "encoding": "base64" }
  ]
}
```

## Parameters

| Parameter    | Type   | Required | Description                                            |
| ------------ | ------ | -------- | ------------------------------------------------------ |
| walletPubkey | string | Yes      | Base58-encoded wallet public key                       |
| filter       | object | Yes      | One of `{ "mint": "..." }` or `{ "programId": "..." }` |
| `encoding`   | string | No       | `"base64"` (default), `"base58"`, or `"base64+zstd"`   |
| `dataSlice`  | object | No       | `{ offset, length }`                                   |

### Filter Options

**By Mint** — returns only token accounts for a specific mint:

```json
{ "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }
```

**By Program** — returns token accounts for a specific token program:

```json
{ "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }
```

When using `mint` filter, both Token Program and Token-2022 are searched automatically.

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "pubkey": "TokenAccountPubkey...",
        "account": {
          "data": ["base64-data...", "base64"],
          "executable": false,
          "lamports": 2039280,
          "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "rentEpoch": 361,
          "space": 165
        }
      }
    ]
  }
}
```

## Example

```bash
curl -X POST https://rpc.thornode.io/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenAccountsByOwner",
    "params": [
      "YourWalletPubkey...",
      { "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" },
      { "encoding": "base64" }
    ]
  }'
```


# getTokenAccountsByOwnerV2

Paginated version of `getTokenAccountsByOwner` with cursor-based navigation and total count.

> **Note:** Large V1 `getTokenAccountsByOwner` queries are automatically rejected and redirected here. If you received a `-32600` error from V1, switch to this method with `limit` and `paginationKey` parameters.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenAccountsByOwnerV2",
  "params": [
    "<walletPubkey>",
    { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
    {
      "encoding": "base64",
      "limit": 200,
      "paginationKey": null
    }
  ]
}
```

## Parameters

| Parameter          | Type    | Required | Description                                            |
| ------------------ | ------- | -------- | ------------------------------------------------------ |
| `walletPubkey`     | string  | Yes      | Base58-encoded wallet public key                       |
| filter             | object  | Yes      | One of `{ "mint": "..." }` or `{ "programId": "..." }` |
| `encoding`         | string  | No       | `"base64"` (default), `"base58"`, or `"base64+zstd"`   |
| `dataSlice`        | object  | No       | `{ offset, length }` — limit returned data bytes       |
| `limit`            | integer | No       | Page size, 1–10,000 (default 1,000)                    |
| `paginationKey`    | string  | No       | Cursor from previous response to fetch the next page   |
| `changedSinceSlot` | integer | No       | Only return accounts updated after this slot           |

### Filter Options

**By Mint** — returns only token accounts for a specific mint:

```json
{ "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }
```

**By Program** — returns token accounts for a specific token program:

```json
{ "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }
```

When using `mint` filter, both Token Program and Token-2022 are searched automatically.

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "pubkey": "TokenAccountPubkey...",
        "account": {
          "data": ["base64-data...", "base64"],
          "executable": false,
          "lamports": 2039280,
          "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "rentEpoch": 361,
          "space": 165
        }
      }
    ],
    "paginationKey": "LastPubkeyInPage...",
    "totalResults": 4523
  }
}
```

| Field           | Type           | Description                                              |
| --------------- | -------------- | -------------------------------------------------------- |
| `value`         | array          | Token account objects for this page                      |
| `paginationKey` | string or null | Cursor for next page. `null` means this is the last page |
| `totalResults`  | integer        | Total number of matching token accounts                  |

## Pagination Example

**Page 1:**

```json
{
  "method": "getTokenAccountsByOwnerV2",
  "params": [
    "YourWalletPubkey...",
    { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
    { "encoding": "base64", "limit": 200 }
  ]
}
```

**Page 2 (using paginationKey from page 1):**

```json
{
  "method": "getTokenAccountsByOwnerV2",
  "params": [
    "YourWalletPubkey...",
    { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
    { "encoding": "base64", "limit": 200, "paginationKey": "LastPubkeyFromPage1..." }
  ]
}
```

## Example

```bash
curl -X POST https://rpc.thornode.io/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenAccountsByOwnerV2",
    "params": [
      "YourWalletPubkey...",
      { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
      { "encoding": "base64", "limit": 100 }
    ]
  }'
```


# getTokenAccountsByDelegate

Returns all SPL Token accounts where the specified wallet is set as the delegate. Searches both the Token Program and Token-2022 Program.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenAccountsByDelegate",
  "params": [
    "<delegatePubkey>",
    { "mint": "<mintPubkey>" },
    { "encoding": "base64" }
  ]
}
```

## Parameters

| Parameter      | Type   | Required | Description                                            |
| -------------- | ------ | -------- | ------------------------------------------------------ |
| delegatePubkey | string | Yes      | Base58-encoded delegate wallet public key              |
| filter         | object | Yes      | One of `{ "mint": "..." }` or `{ "programId": "..." }` |
| `encoding`     | string | No       | `"base64"` (default), `"base58"`, or `"base64+zstd"`   |
| `dataSlice`    | object | No       | `{ offset, length }`                                   |

### Filter Options

**By Mint:**

```json
{ "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }
```

**By Program:**

```json
{ "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }
```

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "pubkey": "TokenAccountPubkey...",
        "account": {
          "data": ["base64-data...", "base64"],
          "executable": false,
          "lamports": 2039280,
          "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "rentEpoch": 361,
          "space": 165
        }
      }
    ]
  }
}
```


# getTokenAccountsByDelegateV2

Paginated version of `getTokenAccountsByDelegate` with cursor-based navigation and total count.

> **Note:** Large V1 `getTokenAccountsByDelegate` queries are automatically rejected and redirected here. If you received a `-32600` error from V1, switch to this method with `limit` and `paginationKey` parameters.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenAccountsByDelegateV2",
  "params": [
    "<delegatePubkey>",
    { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
    {
      "encoding": "base64",
      "limit": 200,
      "paginationKey": null
    }
  ]
}
```

## Parameters

| Parameter          | Type    | Required | Description                                            |
| ------------------ | ------- | -------- | ------------------------------------------------------ |
| `delegatePubkey`   | string  | Yes      | Base58-encoded delegate wallet public key              |
| filter             | object  | Yes      | One of `{ "mint": "..." }` or `{ "programId": "..." }` |
| `encoding`         | string  | No       | `"base64"` (default), `"base58"`, or `"base64+zstd"`   |
| `dataSlice`        | object  | No       | `{ offset, length }` — limit returned data bytes       |
| `limit`            | integer | No       | Page size, 1–10,000 (default 1,000)                    |
| `paginationKey`    | string  | No       | Cursor from previous response to fetch the next page   |
| `changedSinceSlot` | integer | No       | Only return accounts updated after this slot           |

### Filter Options

**By Mint:**

```json
{ "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }
```

**By Program:**

```json
{ "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }
```

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "pubkey": "TokenAccountPubkey...",
        "account": {
          "data": ["base64-data...", "base64"],
          "executable": false,
          "lamports": 2039280,
          "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "rentEpoch": 361,
          "space": 165
        }
      }
    ],
    "paginationKey": "LastPubkeyInPage...",
    "totalResults": 14
  }
}
```

| Field           | Type           | Description                                              |
| --------------- | -------------- | -------------------------------------------------------- |
| `value`         | array          | Token account objects for this page                      |
| `paginationKey` | string or null | Cursor for next page. `null` means this is the last page |
| `totalResults`  | integer        | Total number of matching token accounts                  |

## Pagination Example

**Page 1:**

```json
{
  "method": "getTokenAccountsByDelegateV2",
  "params": [
    "DelegatePubkey...",
    { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
    { "encoding": "base64", "limit": 200 }
  ]
}
```

**Page 2 (using paginationKey from page 1):**

```json
{
  "method": "getTokenAccountsByDelegateV2",
  "params": [
    "DelegatePubkey...",
    { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
    { "encoding": "base64", "limit": 200, "paginationKey": "LastPubkeyFromPage1..." }
  ]
}
```


# getTokenLargestAccounts

Returns the 20 largest token holders for a given mint address.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenLargestAccounts",
  "params": ["<mintPubkey>"]
}
```

## Parameters

| Parameter  | Type   | Required | Description                    |
| ---------- | ------ | -------- | ------------------------------ |
| mintPubkey | string | Yes      | Base58-encoded mint public key |

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "address": "LargestHolderPubkey...",
        "amount": "1000000000",
        "decimals": 6,
        "uiAmount": 1000.0,
        "uiAmountString": "1000"
      },
      {
        "address": "SecondLargestPubkey...",
        "amount": "500000000",
        "decimals": 6,
        "uiAmount": 500.0,
        "uiAmountString": "500"
      }
    ]
  }
}
```

## Response Fields

| Field            | Type    | Description                     |
| ---------------- | ------- | ------------------------------- |
| `address`        | string  | Token account public key        |
| `amount`         | string  | Raw token amount (as string)    |
| `decimals`       | integer | Token decimals from the mint    |
| `uiAmount`       | number  | Human-readable amount           |
| `uiAmountString` | string  | Human-readable amount as string |

## Example

```bash
curl -X POST https://rpc.thornode.io/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenLargestAccounts",
    "params": ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"]
  }'
```


# getLargestAccounts

Returns the 20 largest accounts by lamport balance across the entire ledger.

## Request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getLargestAccounts",
  "params": []
}
```

## Parameters

None.

## Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "context": { "slot": 123456789 },
    "value": [
      {
        "address": "LargestAccountPubkey...",
        "lamports": 99999999999999
      },
      {
        "address": "SecondLargestPubkey...",
        "lamports": 88888888888888
      }
    ]
  }
}
```

## Response Fields

| Field      | Type    | Description                 |
| ---------- | ------- | --------------------------- |
| `address`  | string  | Account public key          |
| `lamports` | integer | Account balance in lamports |


# REST API

In addition to JSON-RPC, a REST endpoint is available for `getProgramAccounts`.

## GET /getProgramAccounts/{programId}

### Query Parameters

| Parameter      | Type    | Description                                          |
| -------------- | ------- | ---------------------------------------------------- |
| `encoding`     | string  | `"base64"` (default), `"base58"`, or `"base64+zstd"` |
| `dataSize`     | integer | Filter by account data size in bytes                 |
| `limit`        | integer | Max results per page (1–10,000)                      |
| `offset`       | integer | Skip N results (for offset-based pagination)         |
| `after`        | string  | Cursor pubkey (for cursor-based pagination)          |
| `since_slot`   | integer | Only return accounts updated after this slot         |
| `slice_offset` | integer | Data slice start offset                              |
| `slice_length` | integer | Data slice byte length                               |

### Response

```json
{
  "result": [
    {
      "pubkey": "AccountPubkey...",
      "account": {
        "data": ["base64-data...", "base64"],
        "executable": false,
        "lamports": 2039280,
        "owner": "ProgramPubkey...",
        "rentEpoch": 361,
        "space": 165
      }
    }
  ],
  "count": 100,
  "cached": true,
  "next": "LastPubkeyInPage..."
}
```

| Field    | Type           | Description                              |
| -------- | -------------- | ---------------------------------------- |
| `result` | array          | Account objects                          |
| `count`  | integer        | Number of results in this response       |
| `cached` | boolean        | Always `true` when served from cache     |
| `next`   | string or null | Cursor for next page. `null` = last page |

### Example

```bash
# Get first 100 SPL token accounts (dataSize=165)
curl "https://rpc.thornode.io/getProgramAccounts/TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA?dataSize=165&limit=100&encoding=base64"

# Get next page using cursor
curl "https://rpc.thornode.io/getProgramAccounts/TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA?dataSize=165&limit=100&after=LastPubkey..."
```

## GET /health

Returns service health status.

### Response

```json
{
  "programs": [
    {
      "program_id": "...",
      "name": "...",
      "snapshot_done": true,
      "snapshot_slot": 280000000,
      "last_grpc_slot": 280001234
    }
  ],
  "grpc_tracking": ["program1...", "program2..."],
  "total_accounts_estimate": 1000000000
}
```


# ShredReplay

ShredReplay captures raw Solana shred packets and lets you replay them to any UDP target with precise timing control. The service retains approximately the last 24 hours of shred data, so you can replay any slot or time window from the past day.

Use it to replay historical network data for testing validators, debugging slot behavior, or analyzing shred propagation.

## Authentication

All API endpoints (except health check) require an API key.

Include your key in every request:

```
X-API-Key: YOUR_API_KEY
```

Your API key is provided by the service administrator. Keep it secret.

***

## Browsing Available Data

### List Available Slots

```bash
# List slots (default: first 100)
curl https://shredreplay.thornode.io/v1/slots \
  -H 'X-API-Key: YOUR_KEY'

# Filter by slot range
curl 'https://API_HOST/v1/slots?from=405445570&to=405445600' \
  -H 'X-API-Key: YOUR_KEY'

# Increase limit (max 1000)
curl 'https://API_HOST/v1/slots?from=405445570&limit=500' \
  -H 'X-API-Key: YOUR_KEY'
```

```json
[
  { "slot": 405445570, "segment_count": 1 },
  { "slot": 405445571, "segment_count": 1 },
  { "slot": 405445572, "segment_count": 1 }
]
```

### Inspect a Slot

```bash
curl https://shredreplay.thornode.io/v1/slots/405445570 \
  -H 'X-API-Key: YOUR_KEY'
```

```json
{
  "slot": 405445570,
  "packet_count": 1536,
  "first_recv_ns": 1773335147575703921,
  "last_recv_ns": 1773335147898216408,
  "first_seq_no": 2115839,
  "last_seq_no": 2117374,
  "variant_histogram": {
    "merkle_data": 768,
    "merkle_coding": 768
  }
}
```

| Field                            | Description                                |
| -------------------------------- | ------------------------------------------ |
| `packet_count`                   | Total shred packets captured for this slot |
| `variant_histogram`              | Breakdown by shred type                    |
| `first_recv_ns` / `last_recv_ns` | Capture time window (unix nanoseconds)     |

***

## Replaying Shreds

### Creating a Replay Job

```bash
curl -X POST https://shredreplay.thornode.io/v1/replay/jobs \
  -H 'X-API-Key: YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "selector": { ... },
    "timing": { "mode": "original" },
    "target": "YOUR_IP:PORT",
    "dry_run": false
  }'
```

```json
{
  "job_id": "87549772-7f81-4425-92ee-01b49d6ac076",
  "status": "queued"
}
```

### Packet Selection

Use the `selector` object to choose which packets to replay.

#### By Slot Range

```json
{
  "selector": {
    "slot_range": [405445570, 405445580]
  }
}
```

#### By Specific Slots

```json
{
  "selector": {
    "slot_list": [405445570, 405445575, 405445580]
  }
}
```

#### By Time (Last N Seconds)

```json
{
  "selector": {
    "last_seconds": 300
  }
}
```

Replays the last 5 minutes of captured data.

#### By Exact Time Range

```json
{
  "selector": {
    "time_range": [1773335147575703921, 1773335149898216408]
  }
}
```

Time values are unix nanoseconds. You can find these from the slot inspect or stats endpoints.

#### With Slot Padding

Include neighboring slots around your selection:

```json
{
  "selector": {
    "slot_range": [405445600, 405445600],
    "padding_slots": 5
  }
}
```

Selects slots 405445595 through 405445605.

#### Filter by Shred Type

```json
{
  "selector": {
    "slot_range": [405445570, 405445580],
    "variant_filter": "data_only"
  }
}
```

| Value           | Description               |
| --------------- | ------------------------- |
| `"all"`         | All shred types (default) |
| `"data_only"`   | Only data shreds          |
| `"coding_only"` | Only coding/FEC shreds    |

#### Only Valid Shreds

```json
{
  "selector": {
    "slot_range": [405445570, 405445580],
    "valid_shred_only": true
  }
}
```

Excludes packets that failed shred header parsing.

### Timing Modes

#### Original Timing

```json
{ "timing": { "mode": "original" } }
```

Preserves the original inter-packet delays. Gaps larger than 1ms are accurate within 10%. Use this to simulate real network conditions.

#### Scaled Timing

```json
{ "timing": { "mode": "scaled", "speed": 2.0 } }
```

| Speed  | Effect                   |
| ------ | ------------------------ |
| `2.0`  | 2x faster than original  |
| `0.5`  | Half speed (slow motion) |
| `10.0` | 10x faster               |

#### Max Speed

```json
{ "timing": { "mode": "max_speed" } }
```

Sends all packets as fast as possible while preserving order. No inter-packet delay.

### Dry Run

Preview how many packets would be replayed without sending anything:

```json
{
  "selector": { "slot_range": [405445570, 405445580] },
  "timing": { "mode": "max_speed" },
  "target": "127.0.0.1:9999",
  "dry_run": true
}
```

### Checking Job Status

```bash
curl https://shredreplay.thornode.io/v1/replay/jobs/JOB_ID \
  -H 'X-API-Key: YOUR_KEY'
```

```json
{
  "job_id": "87549772-7f81-4425-92ee-01b49d6ac076",
  "status": "completed",
  "timing_mode": "max_speed",
  "target": "YOUR_IP:PORT",
  "dry_run": false,
  "selected_count": 8000,
  "sent_count": 8000,
  "time_span": [1773335147575703921, 1773335149898216408],
  "started_at": 1773335200000000000,
  "completed_at": 1773335200500000000,
  "error": null
}
```

| Status      | Meaning                            |
| ----------- | ---------------------------------- |
| `queued`    | Waiting to start                   |
| `running`   | Sending packets                    |
| `completed` | All packets sent                   |
| `failed`    | Error occurred (see `error` field) |
| `cancelled` | Cancelled by user                  |

### Cancelling a Job

```bash
curl -X POST https://shredreplay.thornode.io/v1/replay/jobs/JOB_ID/cancel \
  -H 'X-API-Key: YOUR_KEY'
```

***

## Ordering Guarantees

* Packets are sent in the exact order they were originally captured (sorted by receive timestamp, then sequence number)
* Payloads are byte-identical to the original UDP packets — no modification

***

## Typical Workflow

{% stepper %}
{% step %}

### Browse available slots

GET /v1/slots?from=X\&to=Y → see what's captured
{% endstep %}

{% step %}

### Inspect the slots you care about

GET /v1/slots/405445570 → packet count, variant breakdown
{% endstep %}

{% step %}

### Dry run to preview

POST /v1/replay/jobs → dry\_run: true, see selected\_count
{% endstep %}

{% step %}

### Replay for real

POST /v1/replay/jobs → dry\_run: false, packets sent to your target
{% endstep %}

{% step %}

### Monitor progress

GET /v1/replay/jobs/{id} → watch sent\_count increase
{% endstep %}
{% endstepper %}

***

## Rate Limits

API requests are rate-limited per key. If you exceed the limit, you'll receive a `429 Too Many Requests` response. Wait briefly and retry.

## Health Check

```bash
curl https://shredreplay.thornode.io/healthz
```

Returns `{"ok": true}` — no authentication required.

***

## API Reference

| Method | Path                          | Auth    | Description                                   |
| ------ | ----------------------------- | ------- | --------------------------------------------- |
| `GET`  | `/healthz`                    | No      | Health check                                  |
| `GET`  | `/v1/slots`                   | API Key | List available slots (`?from=X&to=Y&limit=N`) |
| `GET`  | `/v1/slots/{slot}`            | API Key | Inspect a specific slot                       |
| `POST` | `/v1/replay/jobs`             | API Key | Create a replay job                           |
| `GET`  | `/v1/replay/jobs/{id}`        | API Key | Get job status                                |
| `POST` | `/v1/replay/jobs/{id}/cancel` | API Key | Cancel a job                                  |


# Tools

Built to make your Solana Node experience easier.

<table data-view="cards"><thead><tr><th>Tool</th><th data-card-target data-type="content-ref">Documentation</th></tr></thead><tbody><tr><td><p><strong>HELM-PS</strong><br><em>High-Efficiency Load Management Proxy Server</em><br>Smart proxy server that distributes your RPC requests across multiple endpoints.</p><ul><li>Load balancing across multiple RPCs</li><li>Automatic rate limiting</li><li>Real-time metrics</li><li>Zero-downtime auto-updates</li></ul><p>Perfect for managing RPC traffic without overloading your endpoints.</p></td><td></td></tr><tr><td><p><strong>ThorStreamer Yellowstone Translator</strong><br><em>Yellowstone-gRPC Compatibility Layer</em><br>Bridge between ThorStreamer and Yellowstone-gRPC format. Use your existing Geyser-compatible applications with ThorStreamer.</p><ul><li>Transaction streaming</li><li>Account update streaming</li><li>Slot status streaming</li><li>Wallet address filtering</li></ul><p>Drop-in compatibility for apps expecting Yellowstone-gRPC format.</p></td><td></td></tr></tbody></table>


# HELM-PS

#### High-Efficiency Load Management Proxy Server

HELM-PS is a powerful proxy server designed to efficiently distribute and manage RPC requests across multiple endpoints. It's perfect for users who need reliable load balancing and rate limiting for their RPC services.\ <br>

<figure><img src="https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-f2e175b2372ca096393ac9ae44a6aa7663565856%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

## Features

* Smart load balancing across multiple endpoints
* Automatic rate limiting
* Real-time metrics monitoring
* Auto-updates with zero downtime
* Configuration hot-reloading

## Performance & Best Practices

### Impact

* **Latency**: Adds minimal overhead (\~0.05ms per request)
* **Resource Usage**: Light on system resources under normal conditions
* **Reliability**: Load balancing and failover features improve overall stability

### Best Practices

* Keep RPS/TPS at least 10% below your endpoint's actual capacity
* For optimal performance with transactions:
  * Avoid "all" strategy for high-volume transaction requests
  * Use "weighted" strategy when endpoints have different capacities
  * Use "normal" strategy when endpoints are equally powerful
* Enable debug mode temporarily to tune your rate limits
* Monitor the metrics to catch potential bottlenecks early

## Getting Started

{% stepper %}
{% step %}
**Download**

Download the latest version of HELM-PS from our releases page.
{% endstep %}

{% step %}
**Extract**

Extract the downloaded file to your preferred location.
{% endstep %}

{% step %}
**Run**

Run the `helm-ps` executable.

The server will start automatically on port 8080 (default).
{% endstep %}
{% endstepper %}

## Configuration

HELM-PS uses two main configuration files:

### config.json

{% code title="config.json" %}

```json
{
    "port": 8080,
    "debugMode": false,
    "seconds": 60,
    "rateLimitEnabled": false,
    "loadBalancing": {
        "sendTransaction": "weighted",
        "heavyRPC": "normal",
        "other": "all"
    }
}
```

{% endcode %}

### rpc.csv

{% code title="rpc.csv" %}

```csv
methodGroup,alias,url,maxRate
sendTransaction,tx-primary,http://rpc1.example.com,20
sendTransaction,tx-secondary,http://rpc2.example.com,10
heavyRPC,heavy-1,http://rpc3.example.com,100
other,general-1,http://rpc4.example.com,40
```

{% endcode %}

## Load Balancing Strategies

HELM-PS supports three load balancing strategies:

* `weighted`: Distributes traffic based on endpoint capacity
* `normal`: Simple round-robin distribution
* `all`: Sends requests to all endpoints and uses the first successful response

## Method Groups

Requests are categorized into three groups:

* `sendTransaction`: Transaction-related methods
* `heavyRPC`: Resource-intensive methods (like getSupply, getProgramAccounts)
* `other`: All other RPC methods

## Monitoring

HELM-PS provides real-time metrics in your terminal:

* Current requests per second (RPS)
* Transactions per second (TPS)
* 60-second moving averages
* Per-endpoint statistics

## Rate Limiting

Enable rate limiting in `config.json` to protect your endpoints from overload. Set individual rate limits per endpoint in `rpc.csv`.

HELM-PS enforces a hard cap of 1000 requests per second per endpoint. This limit cannot be exceeded even if you set a higher `maxRate` in your configuration. This hardcap helps prevent accidental overload of endpoints.

### Custom Error Codes

HELM-PS uses unique HTTP status codes for different rate limit scenarios:

* `439`: Transaction rate limit exceeded (for sendTransaction requests)
* `449`: Request rate limit exceeded (for all other requests)

These custom codes help you distinguish between transaction and general request limits in your error handling.

## Auto-Updates

HELM-PS checks for updates automatically and can seamlessly upgrade itself without losing any requests.

## Debugging

{% stepper %}
{% step %}
**Enable debug mode**

Set `"debugMode": true` in config.json.
{% endstep %}

{% step %}
**Logs**

Check the `logs` directory for detailed logs.
{% endstep %}

{% step %}
**Terminal**

Watch the terminal for real-time debugging information.
{% endstep %}
{% endstepper %}

## Common Issues

{% stepper %}
{% step %}
**"Another instance is already running"**

* Only one instance of HELM-PS can run at a time.
* Check if HELM-PS is already running in your processes.
  {% endstep %}

{% step %}
**"No endpoints configured"**

* Verify your rpc.csv file exists and contains valid endpoints.
* Each method group needs at least one endpoint.
  {% endstep %}

{% step %}
**Rate limit exceeded**

* Check your maxRate settings in rpc.csv.
* Consider increasing limits or adding more endpoints.
  {% endstep %}
  {% endstepper %}

### License

Apache License 2.0 - See LICENSE file for details.

### Disclaimer

This software is provided "as is", without warranty of any kind, express or implied. The authors and maintainers are not responsible for any damages or losses that may arise from its use. Users should thoroughly test the gateway in their specific environment before deploying to production.

### Contact

For additional support or questions, please contact our support team via our discord server.


# ThorStreamer Yellowstone Translator

A high-performance gateway service that transforms [ThorStreamer](https://github.com/thorlabsDev/ThorStreamer) events into [Yellowstone-gRPC](https://github.com/rpcpool/yellowstone-grpc) compatible format. This service acts as a bridge between ThorStreamer transaction streaming service and applications expecting Geyser-compatible data format.

{% hint style="warning" %}
Performance Notice

While this gateway provides compatibility with Yellowstone-gRPC/Geyser format, it introduces additional latency compared to native ThorStreamer integration. For optimal performance, we strongly recommend using ThorStreamer's native support in your applications when possible.
{% endhint %}

{% hint style="info" %}
Contact

This documentation provides comprehensive technical information for integrating with ThorStreamer. For additional support or questions, please contact our support team via our [discord server](https://discord.gg/thorlabs).
{% endhint %}

## Features

* Real-time transaction streaming
* Wallet address filtering
* Token program tracking
* Health check endpoint
* TLS support
* Configurable logging levels

## Supported Subscription Methods

This gateway currently supports the following [Yellowstone-gRPC](https://github.com/rpcpool/yellowstone-grpc) subscription methods:

### Transactions

```go
map<string, SubscribeRequestFilterTransactions> transactions = 3;
```

* Supports filtering by:
  * Vote transactions (`vote`)
  * Failed transactions (`failed`)
  * Account includes (`account_include`)
  * Account required (`account_required`)
  * Account excludes (`account_exclude`)

### Other Methods

The following methods from Yellowstone-gRPC are **not** currently supported:

* Account subscriptions
* Slot subscriptions
* Block subscriptions
* Block meta subscriptions
* Entry subscriptions
* Ping/Pong requests

We recommend using ThorStreamer's native client for any features not supported by this gateway.

## Installation

{% stepper %}
{% step %}
Download the appropriate binary for your platform from the Releases section.
{% endstep %}

{% step %}
Extract the zip file.
{% endstep %}

{% step %}
Configure `config.json` with your settings.
{% endstep %}

{% step %}
Run the gateway service.
{% endstep %}

{% step %}
Configure your Yellowstone-gRPC client to connect to this gateway (default: `localhost:50052`).
{% endstep %}
{% endstepper %}

## Quick Start

{% stepper %}
{% step %}
Edit `config.json` and set your ThorStreamer URL and authentication token:

```json
{
  "thor_streamer_addr": "<IP:PORT>",
  "thor_auth_token": "your-token-here"
}
```

{% endstep %}

{% step %}
Run the service:

```bash
# Linux/macOS
./ts2ys-gateway

# Windows
run.bat
```

{% endstep %}
{% endstepper %}

## Configuration

The gateway is configured through `config.json`. Here's an example configuration with explanations for each field:

```json
{
   "listen_address": ":50052",           // Gateway's listen address and port
   "thor_streamer_addr": "<IP:PORT>",    // ThorStreamer service address
   "thor_auth_token": "your-token-here", // Your ThorStreamer authentication token
   "enable_tls": false,                  // Enable/disable TLS for the gateway
   "cert_file": "",                      // Path to TLS certificate file (if TLS enabled)
   "key_file": "",                       // Path to TLS key file (if TLS enabled)
   "debug": false,                       // Enable detailed debug logging
   "health_check_port": 8080             // Port for health check endpoint
}
```

### Configuration Fields

* `listen_address`: The address and port where the gateway will listen for incoming connections. Format is `":<port>"` for all interfaces or `"ip:<port>"` for specific interface.
* `thor_streamer_addr`: The address of your ThorStreamer service. Include both IP/hostname and port.
* `thor_auth_token`: Your authentication token for ThorStreamer service.
* `thor_streamer_type`: Subscription type:
  * `transaction`:(RECOMMENDED) Subscribe to transactions based on program IDs.
  * `wallet`: Subscribe to transactions by wallet addresses.
* `enable_tls`: Set to `true` to enable TLS encryption for incoming connections.
* `cert_file`: Path to your TLS certificate file (required if TLS is enabled).
* `key_file`: Path to your TLS private key file (required if TLS is enabled).
* `debug`: Set to `true` to enable verbose logging for troubleshooting.
* `health_check_port`: Port number for the health check HTTP endpoint.

### Health Check Endpoint

The gateway provides a health check endpoint that can be used to monitor the service status:

```bash
# Check service health
curl http://localhost:8080/healthz

# Response when healthy
200 OK
ok

# Response when not ready
503 Service Unavailable
not ready
```

The health check endpoint is useful for:

* Container orchestration systems (Kubernetes liveness/readiness probes)
* Load balancers
* Monitoring systems
* Service health verification

## Performance Considerations

This gateway performs the following operations which add latency:

* Protocol conversion between ThorStreamer and Yellowstone-gRPC formats
* Additional message routing and filtering
* Data transformation and validation

For latency-sensitive applications, consider implementing direct ThorStreamer integration instead of using this compatibility layer.

## License

Apache License 2.0 - See LICENSE file for details.

## Disclaimer

This software is provided "as is", without warranty of any kind, express or implied. The authors and maintainers are not responsible for any damages or losses that may arise from its use. Users should thoroughly test the gateway in their specific environment before deploying to production.


# Join Us

#### What you get:

Earn revenue share by offering RPC node rentals in your Discord server.

#### What you do:

Invite bots, run one command.

#### Time to setup:

10 minutes.


# Quick Start

* Invite the two Thor Labs bots to your server.
* Run the setup command in a channel dedicated to rentals.
* Test a rental and confirm with Thor Labs.
* Announce and monitor.

<a class="button secondary">Jump to the Checklist</a>


# Setup

{% stepper %}
{% step %}
**Pre-Setup**

* Contact Thor Labs to register your server (provide guild\_id, server name)
* Agree on revenue share percentage
* Create a rental channel in your server
  {% endstep %}

{% step %}
**Invite Bots**

Thor Labs provides two bots for the rental flow.

[ThorRent Bot](https://discord.com/oauth2/authorize?client_id=1163557139911024772) (handles rental creation)

* Permissions: Send Messages, Embed Links, Manage Messages

[Rent Management Bot](https://discord.com/oauth2/authorize?client_id=962776329546248252) (handles IP updates)

* Permissions: Send Messages, Embed Links
  {% endstep %}

{% step %}
**Post Interface**

Run this command in your rental channel to post the rental interface:

```bash
!setup-rental
```

Pin the posted message so users can easily find the rental interface.
{% endstep %}

{% step %}
**Test**

* Click "Rent" → select location → submit test transaction
* DM Rent Management Bot: `!start` → verify dashboard works
* Confirm with Thor Labs that test rental has your guild\_id
  {% endstep %}

{% step %}
**Go Live**

* Announce to your community
* Monitor first rentals
* Provide user support as needed
  {% endstep %}
  {% endstepper %}


# How It Works

Rental Flow:

* User clicks "Rent" in your channel → selects location/period
* Sends SOL to treasury wallet
* Submits transaction ID
* Gets instant access

Management:

* User DMs Rent Management Bot: `!start`
* Can update IP, view rentals, access gRPC tokens

Revenue:

* Every rental is tagged with your guild\_id automatically
* Thor Labs provides monthly revenue reports
* Payments per the agreed schedule

## Pricing

* Flash: 2H: 0.25 SOL | 1D: 0.5 SOL | 1W: 1 SOL | 1M: 2 SOL
* Thor: 2H: 0.5 SOL | 1D: 1 SOL | 1W: 2 SOL | 1M: 4 SOL\
  (Check current prices at Thor Labs Discord Server)

{% hint style="info" %}
Prices are managed centrally by Thor Labs. Your interface updates automatically.
{% endhint %}


# Revenue Tracking

To check stats in your server run:

```
!revenue-stats
```

This shows:

* Total rentals (active/expired)
* Total revenue in SOL
* Total rentals (active/expired)
* Breakdown by period (2H, 1D, 1W, 1M)
* Real-time updates

{% hint style="info" %}
Contact Thor Labs for detailed reports and payment schedules.
{% endhint %}

## Ongoing

* Check stats anytime: `!revenue-stats` (your server admins only)
* If the interface is deleted: Run `!setup-rental` again
* Prices update automatically (no action needed)
* Monthly revenue reports will be provided by Thor Labs
* Payments are made per the agreed schedule

## Contact

* [Thor Labs Discord](https://discord.gg/thorlabs)


# Troubleshooting

<details>

<summary>Interface won't post</summary>

Check the bot permissions and verify the ThorRent Bot is online.

</details>

<details>

<summary>Buttons don't work</summary>

Ask the user to restart Discord (sometimes button state refresh is required).

</details>

<details>

<summary>Rental fails</summary>

Possible causes:

* Wrong amount sent
* Wrong wallet used
* Transaction ID already used

</details>

<details>

<summary>Bot not responding</summary>

Verify the bot is online. Instruct the user to DM the Rent Management Bot directly.

</details>

<details>

<summary>Get help</summary>

Contact the Thor Labs team in the official [Thor Labs Discord](https://discord.gg/thorlabs).

</details>


# Setup Checklist

## Pre-Setup

* [ ] Contact Thor Labs (provide guild\_id, server name, contact)
* [ ] Agree on revenue share %
* [ ] Create rental channel
* [ ] Get bot invite links from Thor Labs

## Setup Day

{% stepper %}
{% step %}
**Bots**

* [ ] Invite [ThorRent Bot](https://discord.com/oauth2/authorize?client_id=1163557139911024772) (permissions: Send Messages, Embed Links, Manage Messages)
* [ ] Invite [Rent Management Bot](https://discord.com/oauth2/authorize?client_id=962776329546248252) (permissions: Send Messages, Embed Links)
* [ ] Verify both bots are online
  {% endstep %}

{% step %}
**Launch**

* [ ] Run the setup command in the rental channel:

```
!setup-rental
```

* [ ] Verify the interface posts with buttons
* [ ] Pin the message
* [ ] Receive confirmation DM
  {% endstep %}

{% step %}
**Test**

* [ ] Test rental: Click "Rent" → select location → submit test TX
* [ ] Test management: DM bot: `@Renter Dashboard #4468`

```
!start
```

→ verify dashboard

* [ ] Confirm test rental has your guild\_id (ask Thor Labs)
  {% endstep %}

{% step %}
**Go Live**

* [ ] Delete and get a refund for test rental
* [ ] Announce to community
* [ ] Monitor first rental
  {% endstep %}
  {% endstepper %}

## Ongoing

* [ ] Anytime: Check stats with:

```
!revenue-stats
```

* [ ] Monthly: Review revenue report
* [ ] As needed: Run `!setup-rental` if interface deleted
* [ ] Support: Help users with general questions. Forward them to Thor Labs for detailed support.

## Troubleshooting

<details>

<summary>Interface won't post?</summary>

Check bot permissions and verify the bot is online.

</details>

<details>

<summary>Buttons don't work?</summary>

Ask user to restart Discord and try again.

</details>

<details>

<summary>Rental fails?</summary>

Check the amount, wallet, and ensure the TX is not reused.

</details>

<details>

<summary>Bot not responding?</summary>

Users should DM the Rent Management Bot (do not send the command in a server channel).

</details>

## Quick Reference

```
!setup-rental
```

(Admin only, posts interface)

```
!revenue-stats
```

(Admin only, shows stats)

***

✅ Check off items as completed


# ThorNode Cloud


# Terms of Service

TERM OF SERVICE

AGREEMENT TO TERMS<br>

These terms of use (the “Terms”) are a legally binding agreement shall be considered as executed by and between 100x Studios LTD and you, whether personally or on behalf of an entity (“you”, "client", "user", “customer”, "their", or "your") and 100X Studios LTD, ThorNode Cloud. ("Company", “we”, “us”, or “our”), concerning your access to and use of the cloud.thornode.io website as well as any other media form, media channel, mobile website, service, IaaS rental, subscription or mobile application related, linked, or otherwise connected thereto (defined hereby as the “Site” or as “service” from now on).These Terms also govern your use of all the text, data, information, software, graphics, proprietary content and more (all of which we refer to as “Materials”) that we and/or our affiliates may make available to you, as well as any services we may provide through this Site). Collectively, the Site, the Materials, and the services provided therein are referred to as the “Services”. You agree that by accessing the service, you have read, understood, and agree to be bound by all of these Terms of Use. The Terms explain how you are permitted to use the services provided by and through our platform and website(s).

USING THE SERVICES INDICATES THAT YOU HAVE BOTH READ AND ACCEPT THESE TERMS. IF YOU DO NOT AGREE WITH ALL OF THESE TERMS OF USE, THEN YOU ARE EXPRESSLY PROHIBITED FROM USING THE SITE/SERVICE AND YOU HAVE TO REFRAIN TO USE THE SERVICES FORTHWITH..

<br>

Supplemental terms and conditions or documents that may be posted on the Site from time to time are hereby expressly incorporated herein by reference. We reserve the right, in our sole discretion, to make changes or modifications to these Terms of Use at any time and for any reason. We will alert you about any changes by updating the “Last updated” date of these Terms of Use, and you waive any right to receive specific notice of each such change. It is your responsibility to periodically review these Terms of Use to stay informed of updates. You will be subject to and will be deemed to have been aware of and construed to have accepted, the changes in any revised Terms of Use by your continued use of the service following the date such revised Terms of Use are updated.

The information provided on the Site is not intended for distribution to or use by any person or entity in any jurisdiction or country where such distribution or use would be contrary to law or regulation or which would subject us to any registration requirement within such jurisdiction or country. Accordingly, those persons who choose to ccess the Site from other locations, by doing so , they shall be considered that they had done on their own initiative and they would solely be responsible for compliance with local laws, if and to the extent local laws are applicable.

<br>

APPLICATION AND ACCEPTANCE OF THE TERMS

<br>

Your use of the 100X Studios LTD Services,you ensure us that you represent and warrant that you have a legal capacity to make any transaction independently under the laws of your jurisdiction and/or lawfully you are able to enter into contracts. If you are not legally able to enter into contracts, you may not use the Services at any time or in any manner, or submit any information to 100X Studios Ltd. or the Services.

By accessing the ThorNode Cloud Platform or using the Services provided by 100X Studios you agree to accept and to be bounded by the Terms. Please do not use the Services if you do not agree on and accept all of the Terms.

PRIVACY POLICY

Our Privacy Policy governs the collection, use, and disclosure of personal information we receive from users of our Services. By using our Services, you agree to be bound by our Privacy Policy posted on the Site, which is incorporated into these Terms of Use. Please read our Privacy Policy then through your continued use of the site/services.

In addition to the matters set out in the Privacy Policy in relation to personal data, you agree as follows in relation to any data or information (other than personal data) that you provide to us for processing, storage, hosting or any other purposes in connection with your purchase and use of our Services (“Information”).

You acknowledge and agree that Information related to your payment instruments (such as credit cards), including Information about your payment instrument organisation, the last few digits of your payment instrument number, the security code, and the expiration date of your payment instrument will be transferred to, stored and processed by our third party payment service provider directly in order for them to process your payment transactions and we will generally not store any such Information.

With respect to any other Information that you provide to us or collected by us, including Information provided at registration, Information we record pertaining to your activities, and Information provided voluntarily by you, you acknowledge and agree that we may in our commercial discretion store and/or transfer such Information to any of our affiliates, including those located in other countries.

We will not disclose such Information outside of us, our affiliates or our third party service providers unless:

\- you request us to do so

\- your end user has provided consent for us to do so

-as provided in these Terms of Use or in accordance with your agreement(s) with us,

-as required by and to comply with applicable law, legal process or lawful government requests, or in respect of any claims or potential claims brought against us or our shareholders, subsidiaries or affiliates.

<br>

MODIFICATION

100X Studios Ltd’s and ThorNode Cloud Platforms Terms of Service are both subject to change at any time by posting the updated Terms on the Site and providing a notice on the Services.

A change in Terms shall not be grounds for early contract termination or non-payment. Client recognizes that the nature of the service supplied, and the initial rates and charges have been communicated to the client. The client is aware that from time-to-time rates may change based on availability of hardware, overall market conditions or other factors. Clients will be notified of any increases in rates or charges prior to the billing renewal date on which such increases will take effect.

If you do not agree with any of the updated Terms, you must stop using the Services. Unless otherwise required by law, the updated Terms are effective as of the day of posting.

<br>

100X Studios may make changes to the Services at any time, without any notice to you. If you object to any changes to the Services, your only recourse will be to stop using the Services. Continued use of the Services following posting of any such changes will indicate your acknowledgement of such changes and contentment with the Services as modified. We also reserve the right to discontinue the Services, or any component of it, at any time without notice to you. We will not be liable to you or any third party should we exercise our right to modify or discontinue the Services.

<br>

USERS

<br>

A)USER REPRESENTATIONS

In registering for the Services, you agree to (1) provide true, accurate, current and complete information about yourself; (2) you will maintain the accuracy of such information and promptly update such registration information as necessary; (3) you have the legal capacity and you agree to comply with these Terms of Use; (4) you are not a minor in the jurisdiction in which you reside, or if a minor, you have received parental permission to use the Site; (5) you will not access the Site through automated or non-human means, whether through a bot, script or otherwise; (6) you will not use the Site for any illegal or unauthorized purpose; and (7) your use of the Site will not violate any applicable law or regulation.

If you provide any information that is untrue, inaccurate, not current or incomplete, or 100X Studios Ltd has reasonable grounds to suspect that such information is untrue, inaccurate, not current or incomplete, we have the right to suspend or terminate your Account and refuse any and all current or future use of the Services (or any portion thereof) for breach. You agree not to create an Account using a false identity or information. You agree that you shall not have more than one Account at any given time. You agree not to create an Account or use the Services if you have been previously removed by Us, or if you have been previously banned from any of the 100X Studios Ltd. properties.

<br>

B) USER REGISTRATION

You shall be required to register with the our site/services. You will choose a login (email) and password. You are entirely responsible for maintaining the confidentiality of Your password and for any and all activities which occur using Your credentials and/or under your Account. We reserve the right to remove or reclaim or change any usernames that you selected, (if we determine, in our only discretion, that such username is inappropriate, obscene, or otherwise objectionable) at any time and for any reason, including but not limited to, claims by a third party that a username violates the third party’s rights.

\ <br>

SUBSCRIPTIONS AND CANCELLATIONS

A)SUBSCRIPTIONS

Your Subscription or commencement of the Services shall begin upon confirmation to You and receipt of lawful funds, whichever comes earlier. The Subscription initial term's length is chosen by You and shall be indicated when You subscribe to Our Services. The Subscription may not be terminated by You during the Initial Term (and any renewals thereof) except in the event of a breach by Us. After the Initial Term, the Subscription shall automatically renew for successive terms, equal in length to the Initial Term, unless terminated or canceled by either party as provided herein.

100XStudios Ltd. reserves the right to modify pricing at any time (but not the price in effect for your then-current Subscription Term), upon advance notice to you. If you have not cancelled your Subscription or turned off the auto-renew function within the specified time after receiving notice of a price change, your Subscription will auto-renew at the price indicated in your notice.

B) CANCELLATION

You can cancel your subscription at any time by logging into your account or contacting us using the contact information provided below. Your cancellation/service termination will take effect at the end of the current paid term.You agree and understand that you will be charged Subscription fees until the expiration of your then-current Subscription Term and SUBSCRIPTION FEES WILL NOT BE REFUNDED, IN WHOLE OR IN PART, SUBJECT TO APPLICABLE LAW. You will not be eligible for a pro-rated refund of any portion of the Subscription fees paid for any unused days of the then-current Subscription Term.

IF YOU CANCEL YOUR SUBSCRIPTION, YOUR ACCESS TO AND USE OF THE SERVICES WILL BE SHUT OFF ONCE YOUR THEN CURRENT SUBSCRIPTION TERM EXPIRES.

<br>

PAYMENTS

We offer payment options through Stripe, which of third-party payment provider. We also accept payments through cryptocurrency. By using our Services, you agree to pay the fees associated with your chosen hosting plan, as well as any applicable taxes. We reserve the right to change our prices at any time, but we will provide notice of any changes to you.

<br>

A)VIA CREDIT CARD

If you purchase any of our Services that we offer for a fee, either on a one-time or on a Subscription basis, you agree and consent to Our use of third-party payment providers for billing and processing online payments, and you agree to pay the applicable Fees for the Services (including, without limitation, periodic fees for Subscriptions) as they become due, plus all related taxes, and to reimburse us for all collection costs and interest for any overdue amounts.

<br>

100X Studios Ltd. reserves the right to reject business. This may include any blocking such as IP blocks, address blocks, card blocks, card fingerprint blocks, BIN blocks, e-mail address blocks and more.

You may be required to purchase or pay a fee to access some of our services. You agree to provide current, complete, and accurate purchase and account information for all purchases made via the Site. You further agree to promptly update account and payment information, including email address, payment method, and payment card expiration date, so that we can complete your transactions and contact you as needed. We bill you through an online billing account for purchases made via the Site. Sales tax will be added to the price of purchases as deemed required by us. We may change prices at any time. All payments shall be in U.S. dollars. Incase of making payment with a card with another currency, all bank exchange fees will be paid by the customer. Also with purchasing our service, you agree to pay any and all taxes, including personal property, value added, or sales taxes, resulting from Your use of the Services. 100X Studios is not responsible for any bank fees incurred by You due to Your use of check cards, automatic payment services, insufficient funds, and any and all other fees your financial institution may impose due to Your use of the Services. If 100X Studios should receive less than full payment of the Fees due to taxes, bank charges, transfer fees, or the like, 100X Studios will invoice You for the difference between payment received and the Fees due. Furthermore you agree to pay that you are. responsible for the all attorney and collection fees arising from 100X Studios' efforts to collect any past-due Fees. If your purchase is subject to recurring charges, then you consent to our charging your payment method on a recurring basis without requiring your prior approval Your obligation to pay fees continues through the end of the Subscription Term until you notify us of your cancellation. We reserve the right to charge you until the subscription is terminated by you and correct any errors or mistakes in pricing, even if we have already requested or received payment. We also reserve the right to refuse any order placed through the Site

You must provide complete and valid information regarding your name and address. Credit/debit card information must match your street, CVC/CVV and country when checking out on our website.

You must not use an anonymous IP while checking out on our site.

B)CRYPTOCURRENCY PAYMENTS

All payments will be made with crypto are non-refundable. All payments made with crypto are charged at the current value of the asset in US Dollars. The value of crypto assets can be extremely unstable. Once you have purchased our services with crypto, Our system will automatically exchange the entity to US Dollars.

We will be accepting the following forms of cryptocurrency payments:

* Bitcoin (BTC: on-chain, off-chain with Coinbase, and Lightning)
* Solana (SOL)
* Ethereum (ETH)
* Wrapped SOL (WSOL)
* Dogecoin (DOGE)
* Litecoin (LTC)
* 5 USD-pegged stablecoins (GUSD, USDC, USDT, DAI, and BUSD).

TERMS AND TERMINATION

THESE TERMS OF USE SHALL REMAIN IN FULL FORCE AND EFFECT WHILE YOU USE THE SITE. WITHOUT LIMITING ANY OTHER PROVISION OF THESE TERMS OF USE, WE RESERVE THE RIGHT TO, IN OUR SOLE DISCRETION AND WITHOUT NOTICE OR LIABILITY, DENY ACCESS TO AND USE OF THE SITE (INCLUDING BLOCKING CERTAIN IP ADDRESSES), TO ANY PERSON FOR ANY REASON OR FOR NO REASON, REGULATION, WITH OR WITHOUT NOTICE OR LIABILITY TO YOU, INCLUDING BUT NOT LIMITED FOR BREACH OF ANY REPRESENTATION, WARRANTY, OR COVENANT CONTAINED IN THESE TERMS OF USE OR OF ANY APPLICABLE LAW OR REGULATION. If 100X Studios cancels your Subscription pursuant to any of the terms outlined in these Terms, with the exception of Termination without Cause, 100X Studios shall not refund to You any fees paid or prepaid in advance of such cancellation and You shall be obligated to pay all fees and charges accrued prior to the effectiveness of such cancellation. If 100X Studios terminate your account you will not get any refund.

In addition to 100X Studios’ right to terminate your Subscription provided elsewhere in these Terms, 100X Studiosmay terminate your Subscription effective immediately if, based on 100X Studios’ sole judgment, it determines that You or any of Your end users: (i)have infringed or violated any intellectual property right or privacy or publicity right of a third party, (ii)have not complied with any applicable law, statute or regulation, or100X Studios (iii) have uploaded, published or disseminated any images, text, graphics, code or video which 100X Studios considers illegal or high risk, in its discretion, or (iv) breached these Terms. Nothing contained in these Terms is intended to, or shall, impose any duty or obligation upon 100X Studios to monitor or review Your Content or the content of Your end users at any time. You remain solely responsible for Your Content, and any liability generated therefrom.

Furthermore; you are prohibited from registering and creating a new account under your name, a fake or borrowed name, or the name of any third party, even if you may be acting on behalf of the third party. We do not need to notify the user before the termination of any service.

100X Studios will not have any liability whatsoever to you for any suspension or termination, including for deletion of Content. All provisions of the Terms, which by their nature should survive, shall survive termination of the Service, including without limitation, ownership provisions, warranty disclaimers, and limitation of liability.

The termination of your Subscription will end Your access to the Services and Your license to the Materials. 100X Studios shall not be liable to You or to any third party for termination of the Services permitted under these Terms. Upon termination of your Subscription, 100X Studios reserves the right to maintain copies of Your data files and records for archival purposes but does not undertake any obligation to do so.

USER CONTENT

You agree not to engage in any illegal, harmful, or abusive activities on ThorNode Cloud, including, but not limited to, hacking, spoofing, impersonation, and distributing malware or viruses. We reserve the right to terminate your access to ThorNode Cloud if we suspect any illegal activities.

Your content is important to us, and we want to make sure it is protected. By using our Services, you agree not to use the Services for any illegal activities, including but not limited to terrorism or child pornography. You also agree not to engage in hacking, spoofing, or impersonation. We reserve the right to monitor your content and take appropriate action if we believe it violates these Terms or is otherwise harmful or inappropriate.

<br>

You retain all intellectual property rights to your content, but by using our Services, you grant us a non-exclusive, worldwide, royalty-free, and transferable license to use, reproduce, modify, distribute, display, and perform your content in connection with the Services. You also represent and warrant that you have all necessary rights to grant us this license.

<br>

A)PROHIBITED ACTIVITIES

As a condition of your access to and use of the Services, you agree that you will comply with any and all applicable laws and regulations, and will not engage in fraudulent or deceptive practices, when using the Services.

With respect to content made available via the Services, you agree that you will not:

* Circumvent, disable, or otherwise interfere with security-related features of the Site, including features that prevent or restrict the use or copy, reproduce, download, re-publish, sell, distribute or resell any Services or any information, text, images, graphics, video clips, sound, directories, files, databases or listings, etc made available via the Services of any Content or enforce limitations on the use of the Site and/or the Content contained therein.
* Trick, defraud, or mislead us and other users, especially in any attempt to learn sensitive account information such as user passwords.
* impersonate another person
* Engage in any automated use of the system, such as using scripts to send comments or messages, or using any data mining, robots, or similar data gathering and extraction tools.
* Sell or otherwise transfer your profile.
* You will not collect, use and harvest (or permit anyone else to collect or harvest) any user content or any non-public or personally identifiable information about another user or any other person or entity without their express prior written consent.
* Decipher, decompile, disassemble, or reverse engineer any of the software comprising or in any way making up a part of the Site.
* Harass, annoy, intimidate, or threaten any of our employees or agents engaged in providing service to you.
* infringes any patent, trademark, copyright or other proprietary rights
* Copy or adapt the Site’s software, including but not limited to Flash, PHP, HTML, JavaScript, or other code.
* Upload or transmit (or attempt to upload or to transmit) any kind of viruses, Trojan horses, or other material, including excessive use of capital letters and spamming (continuous posting of repetitive text), that interferes with any party’s uninterrupted use and enjoyment of the Site or modifies, impairs, disrupts, alters, or interferes with the use, features, functions, operation, or maintenance of the Site; Upload or transmit (or attempt to upload or to transmit) any material that acts as a passive or active information collection or transmission mechanism, including without limitation, clear graphics interchange formats (“gifs”), 1×1 pixels, web bugs, cookies, or other similar devices (sometimes referred to as “spyware” or “passive collection mechanisms” or “pcms”)
* Make any unauthorized use of the Site, including collecting usernames and/or email addresses of users by electronic or other means for the purpose of sending unsolicited email, or creating user accounts by automated means or under false pretenses.
* You shall not use any services to send or receive attacks. If so, the service that you take from us, shall be terminated immediately and will be pursued legally. Such attacks include port scanning, e-mail sending, denial of service attacks.

<br>

You may solely use of this site and the services found at this site, including any content you submit will comply with this agreement and all applicable local, state, national and international laws, rules and regulations.

You shall be liable for any damage resulting from any infringement of copyrights, trademarks, proprietary rights, violation of contract, privacy or publicity rights or any other harm resulting from any User Content that you make or submit. As between you and us, you own your User Content and you have full responsibility for all User Content you make or submit, including its legality, reliability and appropriateness, while using the Services.

B)CONTENT

You retain ownership of all content, data, and materials you upload or use with ThorNode Cloud (collectively, “User Content”). You are solely responsible for the User Content you upload or use with ThorNode Cloud. You agree that you will not use ThorNode Cloud to upload or use any illegal, infringing, obscene, or pornographic content. Any use of ThorNode Cloud for any illegal or unauthorized purpose is strictly prohibited.

* Illegal Activity
* You may use Our services for only lawful purpose. Transmission of any material in violation of any Country, Federal, State or Local regulation is prohibited. To this effect, child pornography, and any potential harm to minors using our Services is strictly prohibited. Content that is or may be perceived to be child pornography will be immediately removed from public access upon notification or detection by Us. . Also, using Our servers or network to conspire to commit or support the commission of illegal activities is forbidden as well.
* Also it is prohibited to use our service for; excessively violent, incites violence, threatens violence, or contains harassing content or hate speech; unfair or deceptive under the consumer protection laws of any jurisdiction, including chain letters and pyramid schemes; intended to assist others in defeating technical copyright protections and clearly infringes on another person’s trade or service mark, patent, or other property right; defamatory or violates a person’s privacy; Promotes illegal drugs, violates export control laws, relates to illegal gambling, or illegal arms trafficking; Create a risk to a person’s safety or health, creates a risk to public safety or health, compromises national security, or interferes with an investigation by law enforcement.

DATA LOSS

Your use of our Services mean that you are taking your own risk and you are fully responsible for your own data. We are not liable for any data loss in connection with its services. You are solely responsible for creating backups of Your Content. Provided, however, that during Our own routine maintenance, We do create a backup of Your Content which You later request Us to restore to Your account, We cannot guarantee that we will be able to do so, or that Your Content will be unharmed as a result of the initial data loss or the subsequent restore procedure. To that end, We highly recommend that You establish Your own routine backup procedure and that You periodically test restoring files from Your backup media to ensure that You are making viable backups.

INTELLECTUAL PROPERTY RIGHTS

100X Studios is the sole owner or lawful licensee of all the rights and interests in the ThorNode Cloud and cloud.thornode.io website. To use or reference in any manner Our company names, all titles, logos, product and service names, our proprietary property, all source code, databases, functionality, software, website designs, audio, video, text, photographs, and graphics on the Site, trademarks or services marks or those ownership and Intellectual Property Rights in the ThorNode Cloud shall remain with 100X Studios.

<br>

The unauthorized copying, modification, use or publication of these marks is strictly prohibited. We reserve all rights not expressly granted to you in and to the Site, the Content and the Marks. Any tools or software provided by 100X Studios Ltd. is intellectual property of the company. Any user redistributing the software must contact 100X Studios for permission first. We reserve all rights not to grant you access to redistribute our software. If you breach any of these Terms, the license will terminate automatically and you must stop using the Services and immediately destroy any Materials downloaded or printed from the Service. All Services provided by 100X Studios may only be used for lawful purposes. We reserve all rights not to grant you access to redistribute our software. We give you permission to use the Services and our materials and if we provide you any additional software, these terms apply to it.

“Intellectual Property Rights” shall mean to:

* all rights, title and interest in and to all intellectual property rights, including any and all copyrights, patents, trade marks, service marks, logos, get-up, trade names, internet domain names, rights in designs, rights in computer software, database rights, semi-conductor topography rights, utility models and rights in know-how, in each case whether registrable or not, and including any applications for registration, and all rights or forms of protection having equivalent or similar effect anywhere in the world, and across all platforms and mediums whether now known or in the future invented;
* all rights under licences, consents, orders, statutes or otherwise in relation to any of the rights referenced in sub-paragraph (a) above;
* all rights of the same or similar effect or nature as or to those in sub-paragraphs (a) and (b) which now or in the future may subsist;
* all rights to income, royalties, damages, claims and payments now or hereafter due or payable with respect thereto; and
* all rights at law or in equity to sue for past or future infringements of any of the foregoing rights.

<br>

### LIMITATION ON LIABILITY

100X STUDIOS SHALL NOT BE RESPONSIBLE FOR ANY CLAIMED DAMAGES, INCLUDING INCIDENTAL AND CONSEQUENTIAL DAMAGES, WHICH MAY ARISE FROM 100X STUDIOS SERVERS GOING OFF-LINE OR BEING UNAVAILABLE FOR ANY REASON WHATSOEVER. THIS SECTION APPLIES TO ALL CLAIMS BY YOU OR YOUR END USERS IRRESPECTIVE OF THE CAUSE OF ACTION UNDERLYING THE CLAIM, INCLUDING, BUT NOT LIMITED TO, BREACH OF CONTRACT, TORT, INCLUDING BUT NOT LIMITED TO NEGLIGENCE, STRICT LIABILITY, FRAUD, AND/OR MISREPRESENTATION.FURTHERMORE, 100X STUDIOS SHALL NOT BE RESPONSİBLE FOR ANY CLAİMED DAMAGES, INCLUDİNG INCIDENTAL OR CONSEQUENTIAL DAMAGES, RESULTING FROM THE CORRUPTİON OR DELETION OF ANY WEB SITE FROM ONE OF 100X STUDIOS' SERVERS. ALL DAMAGES SHALL BE LIMITED TO THE IMMEDIATE TERMINATION OF SERVICE. THE SERVICES, TECHNOLOGY, OR CONTENT AVAILABLE ON THE SERVICES, BE LIABLE TO YOU IN ANY MANNER WHATSOEVER: (A) FOR ANY DECISION MADE OR ACTION OR NON-ACTION TAKEN BY YOU IN RELIANCE UPON THE INFORMATION PROVIDED THROUGH THE SERVICES; (B) FOR LOSS OR INACCURACY OF DATA, OR COST OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY; (C) FOR ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, INCLUDING BUT NOT LIMITED TO LOSS OF REVENUES, LOSS OF PROFITS OR LOSS OF REPUTATION, FOR BUSINESS INTERRUPTION OR SIMILAR ACTION, EVEN IF 100X STUDIOS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR (D) FOR YOUR USE OF ANY THIRD-PARTY SERVICES.

THE TOTAL AGGREGATE AND MAXIMUM LIABILITY OF 100X STUDIOS AND THE AFFILIATES, ARISING FROM OR OTHERWISE RELATING TO THIS AGREEMENT (REGARDLESS OF THE FORM OF ACTION OR CLAIM) IS LIMITED TO ANY AMOUNTS YOU HAVE PAID TO 100X STUDIOS DURING THE SIX (6) MONTHS PRIOR TO THE ACCRUAL OF THE CAUSE OR CAUSES OF ACTION.

<br>

GOVERNING LAW AND DISPUTE RESOLUTION

<br>

**A)APPLICABLE LAW: These Terms of Use and your use of the service are governed by and construed in accordance with the laws of England and Wales, which are applicable to all agreements that are planned to be signed together or separately with this Terms of Services following of your approval through putting a check mark on the box below the end of this terms of services and to be entirely performed within the United Kingdom, without regard to its conflict of law principles.**

B)DISPUTE RESOLUTION: You agree that 100X Studios may elect to resolve the dispute in a cost-effective manner through binding arbitration (including non-appearance-based arbitration). The London Court of International Arbitration (LCIA) or or another established alternative dispute resolution provider chosen by Us will be applicable in this case.

<br>

INDEMNIFICATION

You agree to defend, indemnify, and hold Us harmless from and against any and all claims, damages, expenses, and liabilities, including reasonable attorneys' and experts' fees which arising out of your use or misuse of the Services, such as;

-any breach of Your covenants under these Terms

-Your use of the Services

-Any defamatory, libelous or illegal material contained within user content or your information and data

-Any claim or contention that Your Content, Your information and data, or Your use of any Third-Party Services infringes any third party's patent, copyright or other intellectual property rights or violates any third party's rights of privacy or publicity

-Any third party's Access or use of User Content or Your information and data.

We reserve the right to assume control of the defense of any third-party claim that is subject to indemnification by you, in which event you will cooperate with us in asserting any available defenses, which you are not obligated to do so.

DMCA COMPLAINT<br>

If either party becomes aware of any unauthorized use of copyrighted material or other intellectual property rights owned by the other party, the party shall promptly request the removal of those materials (or access to them) from our website or services by submitting written notice to the other party in accordance with the Digital Millennium Copyright Act (DMCA) (17 U.S.C. § 512). The notice shall include the information required by the DMCA, including the following: (a) a description of the copyrighted work that is allegedly being infringed; (b) a description of the infringing material and its location; (c) the name and contact information of the copyright owner; and (d) a statement that the information in the notification is accurate and, under penalty of perjury, that the complaining party is authorized to act on behalf of the copyright owner. Upon receipt of such notice, the party responsible for the infringing material shall remove the material or disable access to it within 14 (fourteen) business days after receipt of the notice. Failure to comply with this clause may result in termination of this Agreement. In accordance with the Digital Millennium Copyright Act (DMCA) and other applicable laws, 100X Studios Ltd has adopted a policy of terminating, in appropriate circumstances and at our sole discretion, users who are deemed to be repeat infringers. 100X Studios Ltd will respond to all such notices, including as required or appropriate by removing the infringing material or disabling all links to the infringing material. 100X Studios Ltd will terminate a user's access to and use of the website or services if, under appropriate circumstances, the user is determined to be a repeat infringer of the copyrights or other intellectual property rights of 100X Studios Ltd or others. In the case of such termination, 100X Studios Ltd will have no obligation to provide a refund of any amounts previously paid to 100X Studios Ltd.

CONTACT US

If you have any questions about these Terms or otherwise need to contact 100X Studios for any complaint regarding the Site/Services, please contact us at:

<br>

100X Studios Contact: <cb@100xstudios.com>

<br>


# ThorNode Cloud - Refund Policy

100X Studios LTD

REFUND POLICY

By purchasing our products, you fully understand the following:

The customer retains the right to terminate their subscription, thereby preventing automatic renewal for the subsequent month or billing cycle. It is expressly stated that all payments made are deemed final, and refunds shall not be granted under any circumstances.

REFUNDS & DISPUTES

100X Studios ("we”) offer digital products, products (“servers”) are active once they are delivered to the customer. We are not responsible for bans during the release for improper use and incorrect setup. By purchasing this item, you agree to including but not limited to our Terms of Service, "No Modifications,", and "No Refund" policy. This item is a non-physical good and access to such good will be sent via the email provided at checkout or directly delivered to your client portal.

If, under extreme circumstances, refunds may be given at our discretion. Any dispute or chargeback made via you, your card issuer, or your bank will lead to a permanent account suspension and instant service termination without warning.

The Customer is fully responsible for securing his hosting account and ensuring that this agreement is not broken by any third party with or without his knowledge or consent.

If we have deemed it necessary or was caused by an issue on our end then any issue will be promptly dealt with, credits/refunds should reflect on your bank statement within 3-5 days.

Credits shall not be provided to you in the event that you have no Web Site Availability resulting from;

* Scheduled maintenance,
* Your behavior or the performance or failure of your equipment, facilities or applications, or
* Circumstances beyond 100X STUDIOS LTD’s reasonable control, including, without limitation, acts of any governmental body, war, insurrection, sabotage, embargo, fire, flood, strike or other labor disturbance, interruption of or delay in transportation, unavailability of interruption or delay in telecommunications or third party services (including DNS propagation), failure of third party software or hardware or inability to obtain raw materials, supplies, or power used in or equipment needed ort he provision of your Web Site.

You can cancel your subscription to the server for the upcoming billing period after logging into your user dashboard. When you subscribe to a service, you will only be charged for the plan you use at your chosen billing frequency.Payments for Upgrades, Addons, Setup fees and Domain Registrations are NOT refundable. Unless you have abused our network you will not be charged for any bandwidth of the server. If you violate any point of our Terms of Service you will NOT be eligible for any refund.

Client will have to just cancel the service. Refunds are not given. If a refund is permitted by an administrator under extreme circumstances mentioned above, prorated refunds are calculated by how many days you have used the service. Administrators reserve the right to charge you an service fee (will be calculated using the full payment of the service), administrative restocking fee of 5.00% of the total order, beside a debit/credit card processing fee of (3.9% + $0.30) before refund process (determined by the administrators).


# ThorNode Cloud - Privacy Policy

PRIVACY POLICY

General

This Privacy Policy (“Policy”) refers to 100X STUDIOS LTD and you, the user of this site.

“Us, We, Our” - 100X STUDIOS LTD is the publisher and operator of the service, and is referred to as “ThorNode Cloud”. “us”, “we”, “our”, “ours”, etc. refer to 100X STUDIOS LTD. “The SITE” or “SITE” refers to ThorNode Cloud website is“cloud.thornode.io”.

“You, the User” - This Policy will refer to the user as “you” or “yours”, etc.

This Policy describes how we use information received about you when you visit our SITE or when you subscribe to, or otherwise use our online services. This Policy does not cover any information that we may receive from or about you through channels other than through the use of the SITE.

Thank you for choosing ThorNode Cloud, the cloud hosting system provided by 100X Studios Company. We take your privacy very seriously, and this Privacy Policy explains how we collect, use, and protect your personal information.

Information We Collect

When you sign up for ThorNode Cloud, we collect the following personal information:

* Name
* Email address
* Phone number
* Payment information and any other necessary billing information
* IP address
* Usage data, such as access times and pages viewed
* automatically collect the cookies, web beacons, and other technologies: your domain name; your browser type and operating system; web pages you view; links you click; your IP address; the length of time you visit or use our SITE; or the webpage that led you to our SITE. We may combine this information with other information that we have collected about you, including, where applicable, your user name, name, and other personal information

We may also collect non-personal information, such as your browser type and version, operating system, and the URL of the website that referred you to our site.

<br>

How We Use Your Information

We use your personal information to:

* Provide and improve our services to you
* Respond to your requests and inquiries
* Communicate with you about our services, promotions, and updates
* tailor the content and information that we may send or display to you, such as displaying information on your use of our cloud services, to offer location customization, and personalized help and instructions, and to otherwise personalize your experiences while using the service
* Administer surveys and questionnaires.
* For marketing and promotional purposes. For example, we may use your information, such as your email address, to send you news and newsletters, special offers, and promotions, or to otherwise contact you about products or information we think may interest you. We also may use the information that we learn about you to assist us in advertising our services on third party websites
* Process payments and prevent fraud
* Enforce our terms and conditions
* Comply with legal obligations, protect the safety, rights, property, or security of Us, our services, any third party, or the general public; to detect, prevent, or otherwise address fraud, security, or technical issues; to prevent or stop activity that 100X Studios, in its sole discretion, may consider to be, or to pose a risk of being, an illegal, unethical, or legally actionable activity; to use as evidence in litigation; to conduct audits; and to enforce this Policy and our Terms of Service
* comply with applicable legal or regulatory obligations, including as part of a judicial proceeding; to respond to a subpoena, warrant, court order, or other legal process; or as part of an investigation or request, whether formal or informal, from law enforcement or a governmental authority

We may also use your non-personal information for statistical purposes, such as analyzing usage patterns and improving our services.

How We Share Your Information

We do not sell, rent, or share your personal information with third parties for their marketing purposes. We may share your information with our service providers who help us provide and improve our services, such as payment processors and customer support providers. We may also share your information to comply with legal obligations, enforce our terms and conditions, or protect our rights and property.

Security

We take reasonable measures to protect your personal information from unauthorized access, use, and disclosure. We use industry-standard encryption and authentication technologies to secure your data, and we store your data in secure servers located in the United Kingdom. Despite our efforts to secure your information, it is not guaranteed %100 security, we cannot promise or guarantee that hackers, cybercriminals, or other unauthorized third parties will not be able to defeat our security, and improperly collect, access, steal, or modify your information. Eventhough we will do our best to protect your personal information, transmission of personal information to and from our Website is at your own risk. You should solely access the Website within a secure environment.

Your Choices

You can manage your communication preferences by logging into your ThorNode Cloud account and updating your notification settings. You can also unsubscribe from our marketing emails by clicking the "unsubscribe" link at the bottom of the email.

Cookies

We use cookies and similar technologies to provide and improve our services, personalize your experience, and analyze usage patterns. You can control cookies through your browser settings.

Children's Privacy

ThorNode Cloud is not intended for children under the age of 13, and we do not knowingly collect personal information from children under the age of 13. If we learn that we have collected personal information from a child under the age of 13, we will take steps to delete the information as soon as possible.

Changes to this Privacy Policy

We may update this Privacy Policy from time to time to reflect changes in our practices or legal obligations. We will notify you of any material changes to this Privacy Policy by email or through our website.

Contact Us

If you have any questions or concerns about this Privacy Policy or our practices, please contact us at <cb@100xstudios.com>


# Introduction to Bots and Nodes

A short brief for bots and nodes.

### Overview

In the world of blockchain and NFT marketplaces, bots and nodes play a critical role in facilitating transactions and maintaining the overall efficiency of the ecosystem. In this guide, we will provide a detailed explanation of what bots and nodes are, how they function, and why they are essential to the smooth operation of these networks.

### Bots

#### What is a bot?

A bot is an automated software application programmed to perform specific tasks without the need for human intervention. Bots can mimic or replace human users' behavior and often execute repetitive tasks much faster and more accurately than humans.

#### Why do we need bots?

Bots are particularly helpful in two primary use cases in the NFT and blockchain space: minting and sniping.

**Minting bot**

Minting refers to the process of creating a new NFT or token on a blockchain platform. Sometimes, users may struggle to mint their desired NFTs due to network congestion or high demand. Minting bots help users overcome this problem by automating the minting process and performing tasks at a speed unattainable by humans. This greatly increases the chances of successfully minting a project, even those with high demand.

**Example**: Imagine a limited edition NFT collection that will be minted on a first-come, first-serve basis. A user may employ a minting bot to automatically submit their transaction to the blockchain as soon as the minting begins, increasing their chances of acquiring the desired NFT.

**Sniping bot**

On secondary NFT marketplaces like MagicEden or OpenSea, price errors may occur, and NFTs may be listed at prices lower than their actual value. In such cases, sniping bots allow users to automatically execute buy orders for NFTs listed below a specific price or the current floor price.

**Example**: A user configures a sniping bot to monitor a particular NFT collection. If an NFT is listed below the specified price threshold, the bot automatically places a buy order, securing the NFT for the user at a bargain price.

### Nodes

#### What is a node?

A node is a computer program that allows users to connect with and interact with a blockchain network. Nodes communicate with other nodes to relay information, validate transactions, and store crucial data about the blockchain's state. A blockchain network is essentially composed of nodes operated by individuals worldwide, making it a decentralized system without a single point of authority.

#### Why do we need nodes?

A node allows users to interact with the blockchain faster than public endpoints, enabling them to execute transactions ahead of others. As a result, users can be the first to mint a project or purchase an NFT at a reduced price on the secondary market.

#### Key points for a good RPC

Remote Procedure Call (RPC) is a powerful technique for creating distributed, client-server based applications. It extends the traditional local procedure calling to enable calling procedures that are not in the same address space. An RPC server provides remote connection and communication services to clients, such as facilitating transactions within a blockchain network.

A good RPC server should have the following qualities:

1. **Reliability**: The RPC server should have a high uptime and be consistently available for clients to connect and execute commands.
2. **Scalability**: The server should be able to handle a large number of connections and process high volumes of requests efficiently.
3. **Security**: The RPC server must have robust security measures in place to protect sensitive data and prevent unauthorized access.
4. **Low latency**: Fast response times are crucial for executing transactions swiftly and staying ahead of the competition.

**Example**: A user sets up a private node with a reliable RPC server for the Solana blockchain network. By doing so, they can seamlessly interact with the Solana network and ensure faster transaction processing for minting projects or purchasing NFTs in the secondary market. This private node's RPC server provides the user with a reliable and secure connection, enabling them to execute commands and transfer data using RPC calls within the Solana ecosystem.

In conclusion, bots and nodes play an essential role in enhancing the user experience within the blockchain and NFT marketplaces. Bots provide automation and speed in executing tasks such as minting and sniping, while nodes offer a faster and more reliable connection to the blockchain network. By utilizing these tools, users can optimize their strategies in the ever-evolving world of NFTs and blockchain technology.


# Frequently Asked Questions (FAQs) for Solana Beginners

These FAQs are designed to help beginner users understand and navigate issues related to rate limits when interacting with ThorNode's Solana RPC nodes and bots.

**1. What is Solana?**

* Solana is a high-speed, high-throughput blockchain known for its unique approach to processing transactions and maintaining network efficiency.

**2. How are transactions confirmed in Solana?**

* Transactions in Solana are confirmed through a process involving validators and RPC (Remote Procedure Call) nodes. A transaction includes a message, a list of signatures, and a recent blockhash. Validators are responsible for producing blocks and validating transactions, while RPC nodes act as intermediaries between clients and validators.

**3. What is the role of a blockhash in Solana transactions?**

* The blockhash in a Solana transaction acts as a timestamp and ensures that transactions are processed within a specific timeframe, preventing double processing and ensuring currentness.

**4. What is a validator and what does it do?**

* Validators in Solana are responsible for producing blocks, validating transactions, and maintaining the blockchain's integrity. They use the Proof of History (PoH) mechanism to establish a trusted sequence of events.

**5. What is an RPC node and its function?**

* RPC nodes serve as intermediaries between clients (like wallets or applications) and validators. They handle client requests for submitting transactions, provide information such as blockhashes and account data, and forward transactions to validators.

**6. How does network congestion affect Solana?**

* During network congestion, when the volume of transactions exceeds what validators can process timely, it can lead to delays in transaction processing, longer confirmation times even drops. Validators may struggle with backlogs, and RPC nodes can experience slower response times and increased latency.

**7. What happens to transactions during congestion?**

* Transactions might expire before being processed due to the recent blockhash mechanism, requiring users to resubmit them. Validators balance processing new transactions with maintaining ledger integrity, which is challenging during peak loads.

**8. Can RPC nodes solve congestion issues?**

* While RPC nodes can optimize client request handling and improve user experience, they cannot independently resolve congestion issues within the blockchain itself. The solution largely relies on the network's scalability and validators' efficiency.

**9. Why is understanding Solana important?**

* Understanding Solana's transaction processing, network congestion, and the roles of validators and RPC nodes is crucial for developers and users navigating this advanced blockchain technology.

**10. What should I do if my transaction is taking too long to confirm on Solana?**

* If your transaction is delayed, check the network's current congestion level. You may need to resubmit your transaction with a newer blockhash if the original one has expired.

**11. How can I check the status of my transaction on the Solana network?**

* You can use a Solana block explorer or your wallet's transaction history to check the status. Look for the transaction ID or signature for detailed information.

**12. What causes a transaction to fail on Solana?**

* Transactions can fail due to reasons like an invalid blockhash (if it's too old), insufficient funds, incorrect account details, or network congestion.

**13.Why does a blockhash expire in Solana?**

* Blockhashes in Solana expire to maintain network security and efficiency. They prevent replay attacks, manage the blockchain state, and ensure transaction relevance by requiring transactions to be processed in a timely manner.

**14.Is the expiration of a blockhash related to RPC nodes or validators?**

* The expiration of a blockhash is more directly related to validators. Validators enforce the blockhash expiration to ensure transaction security and efficiency, whereas RPC nodes facilitate transaction communication without influencing blockhash expiration rules.

**15. Can I cancel a transaction once it's submitted to the Solana network?**

* Once submitted, a transaction cannot be canceled as it's already in the process of being confirmed by validators. It can only fail or succeed.

**16. How do I ensure my transaction is processed quickly on Solana?**

* To expedite processing, ensure your transaction has a recent blockhash and sufficient priority fees. Avoid submitting during peak congestion times if possible or try sending transactions in bulk aka by spamming.

**17. How can I avoid my transaction expiring due to the blockhash being too old?**

* Regularly update the blockhash in your transaction, especially during times of high congestion, to prevent it from expiring.

**18. Can network congestion affect the success rate of my transactions on Solana?**

* Yes, during high congestion, there's a higher chance of transactions expiring or being delayed, which can affect their success rate.

**19. Why is my transaction dropped on the Solana network?**

* Transactions may be dropped due to expired blockhashes, network congestion, insufficient fees, invalid transaction structure, or issues with the RPC node forwarding the transaction.

**20. Why can't I see my transaction on a Solana blockchain explorer?**

* This could be due to propagation delays, searching with incorrect information, dropped transactions, or synchronization issues with the explorer itself.

**21. I sent 50 transactions, but only 3 appear on the blockchain explorer. Why?**

* This discrepancy might be caused by network congestion, batch processing delays by validators, expired blockhashes, or issues with the RPC node forwarding the transaction.

**22. What is the difference between TPS and RPS in blockchain context?**

* TPS (Transactions Per Second) measures the number of transactions a blockchain network can process each second, while RPS (Requests Per Second) refers to the number of requests (including transactions and data queries) that an RPC node or server can handle per second.

**23. What does a 429 Rate Limit Error mean when using Solana RPC nodes?**

* A 429 Rate Limit Error indicates that you have exceeded the number of requests allowed by the RPC node within a given time frame. This is a common rate limiting mechanism to prevent overuse of resources. If you encounter this error, you should reduce the frequency of your requests to stay within the node's limits.

**24. Why am I getting a 502 Timeout Error after receiving a 429 Error on ThorNode RPC?**

* After repeatedly hitting the rate limit and receiving 429 errors, the RPC node may temporarily block your connection as a protective measure, resulting in 502 Timeout Errors. This block is usually temporary (3 minutes), but you must wait for a certain period before attempting to connect again. Ensure to adjust your request rate to avoid future blocks.

**25. How can I avoid exceeding the rate limit on Solana RPC nodes?**

* To avoid exceeding rate limits, you can:
  1. **Optimize Your Requests:** Make your queries more efficient by requesting only the necessary data.
  2. **Implement Caching:** Cache responses locally to reduce the need for repeated requests.
  3. **Use Multiple RPC Nodes:** Distribute your requests across multiple RPC nodes to spread the load.
  4. **Adhere to Node Guidelines:** Familiarize yourself with the specific rate limits of the RPC node you are using and adjust your request patterns accordingly.

**26. What should I do if my access to an RPC node is blocked due to exceeding rate limits?**

* If you're blocked, you should:
  1. **Wait for the Block to Lift:** Blocks are usually temporary (3 minutes), so wait for the specified duration before trying again.
  2. **Review Your Request Patterns:** Understand why your requests exceeded the limit and modify your approach.
  3. **Contact Node Support:** If you believe the block is in error or need more information, contact the support team of the RPC node service.

**27. What does a 403 Error mean when accessing RPC nodes and how can I resolve it?**

* A 403 Error typically indicates that access to the RPC node is forbidden due to authorization issues. To resolve this:
  1. **Authenticate Your IP:** Use the AuthDashboard discord bot to authenticate your IP address.
  2. **Disable IPv6:** If the issue persists, disable IPv6 on your device or network. ThorNode only supports IPv4, so disabling IPv6 can resolve the issue.
  3. **Verify by Opening RPC URL in Browser:** To confirm that the issue is resolved, open the RPC node's URL in your web browser. You should see an "HTTP POST not allowed" error, which is normal for RPC URLs accessed via a browser. If you still see the 403 error, the authorization issue persists.

**28. How can I measure the latency of an RPC node?**

* To measure the latency to an RPC node, you can use the ping command in the command prompt or terminal. Here's how you can do it:
  1. **Open Command Prompt or Terminal:** On Windows, you can open Command Prompt by searching for `cmd` in the start menu. On macOS or Linux, open the Terminal.
  2. **Run the Ping Command:** Type the ping command followed by the RPC node's domain. For example, `ping rpc-de.thornode.io`.
  3. **Analyze the Results:** The output will show the time in milliseconds it takes for a packet to travel from your computer to the RPC node and back (round-trip time). This time represents the latency. Lower values indicate better latency.

**29. What is the Max Compute Limit for each block on Solana?**

* Solana sets a maximum compute limit per block to ensure network stability and efficiency. Each program in Solana, like the TOKEN PROGRAM, has a limit of around 12 million Compute Units (CUs). This limit roughly translates to the capacity for processing about 150 transactions per block for that program.

**30. How is the transaction capacity in the TOKEN PROGRAM distributed among different projects like Orca, Raydium, etc.?**

* The transaction capacity in the TOKEN PROGRAM is shared among various projects and platforms like Orca, Raydium, Lifinity, Meteora, and others. This shared capacity means that during times of high demand, transactions from these different platforms compete for the same limited compute resources.

**31. Why might my transaction fail or not get accepted in a Solana block?**

* On Solana, it's normal for a transaction to fail or not be accepted in a block, especially during times of high network demand. This is due to the compute limit per block and the shared transaction capacity among various projects. It's not necessarily a problem with the node or the network; it's just how Solana currently operates.

**32. What should I consider when setting the compute limit in my transactions on Solana?**

* Be cautious with the compute limit you set in your transactions. Unlike other blockchains like Ethereum or Binance Smart Chain, where you pay for the actual compute used, Solana charges you for the total compute limit set, whether it's fully used or not. Setting it too high can lead to higher transaction fees. For instance, a normal swap on Raydium might use around 90K CUs, but some users set their limit higher to be safe. However, remember that you pay for the total amount set in the compute limit on Solana.

**33. Does changing the default compute limit affect transaction success when sniping on Solana?**

* Adjusting the compute limit can impact your transaction success, especially in high-demand scenarios like token sniping. Setting a higher limit may increase the chances of your transaction being processed, but it also incurs higher fees. It's a balance between ensuring your transaction gets through and managing the cost.

**34. Why is my transaction getting late confirmations?**

1. **Network Congestion:** High network traffic can cause a backlog, leading to delayed confirmations.
2. **Transaction Prioritization:** Transactions with higher fees or simpler computations may be prioritized by validators.
3. **Variability in Validator Performance:** Differences in validators' processing speeds can affect confirmation times.
4. **Blockhash Expiration Risk:** Transactions with near-expiring blockhashes might be deprioritized.
5. **Use of Stale Blockhash:** A blockhash that isn’t the most recent can lead to initial delays.
6. **RPC max\_retry Settings:** Setting `max_retry` or similar setting (for RPC) higher than 0 in your bot configuration can lead to re-broadcasting of the transaction. While this can be useful in ensuring your transaction is eventually processed, it can also lead to unnecessary delays, especially during periods of congestion. Re-broadcasted transactions might be treated as new by the network, pushing back their confirmation.
7. **Setting max\_retry to 0:** To avoid late confirmations, consider setting `max_retry` or similar setting to 0. This prevents the RPC node from re-attempting the transaction submission, which can help in reducing confirmation delays.
8. **Preflight Check Settings:** Setting the preflight check to false can also impact transaction processing. While this can speed up the submission process, it increases the risk of failed or dropped transactions, as it bypasses the initial safety checks.
9. **Balancing Retry Settings:** It's advisable to use the RPC retry feature under normal network conditions. However, during times of high congestion, disabling max retry (`max_retry` set to 0) can be more effective in avoiding late confirmations.

* Remember, these settings can have trade-offs. Disabling retries or preflight checks might reduce delays in confirmations but can increase the likelihood of transaction failures or drops. Adjust these settings based on your specific needs and the current network conditions.

<br>


# AWS EC2 (VPS) Setup

Private RPC users prefer to run their software (bots or sniping tools) in a virtual server which has low latency to the RPC node.

## Why Do I Need a VPS/VM/Server?

You need a fast internet connection and low ping to utilize your software which use private Solana RPCs. Most users do not have low pings to the RPC nodes and have slow internet connection so they prefer using servers in datacenters.

## Why AWS?

AWS has one of the best network routing and they have a variety of server configurations which is what we want for optimal performance. Also pay to go option is quite good for beginners.

### 1. EC2 Setup

* Go to <https://us-east-1.console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstances:>
* Name your server and select OS as given below. Also create a new administrator key as .pem

![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-28de0f26d4e9aada2f9cf7699b7d2f7d2559a52e%2Fimage.png?alt=media)

* Scroll down and select an instance type.

![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-0894dc5eaa0c6b0bfe7d86cc16488e88803387de%2Fimage.png?alt=media)

{% hint style="info" %}
*Minimum 16 vCPU and 32GB ram with 1 GB bandwith is suggested. You may prefer c5a.4xlarge or c5a.8xlarge depending on your budget.*
{% endhint %}

* Change nothing in Network Settings and Configure Store and click "Launch Instance"

### 2. Run your VPS

* Now your VPS is ready to run. Remote Desktop application is used to connect your VPS desktop. You will use Public IPv4 addresses to connect your remote desktop.

![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-7efacf4f36148cfcafe35ef8b8afa5a28665bdc1%2Fimage.png?alt=media)

* Before connection, you need administrator password. Right click on your VPS and:\
  ![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-da792d3f3d58902eceec8f61d5df563b7e09c351%2Fimage.png?alt=media)
* A new window will pop-up. Browse your .pem key file and decrypt the hash. Now you have your administrator password. You will need it when you login your EC2 instance.

![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-a249539c4f26612641272c23db7f9450e4499670%2Fimage.png?alt=media)

### 3. Connect

* Run Remote Desktop software and "add PC" not workspace. A pop-up will show up and enter your credentials:\
  PC Name: Your Public IPv4 Address\
  User Account: "Add user account" for first usage and faster connection.
* ![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-e5d86ff61c980e53bd8608859c7f050c46e10cdf%2Fimage.png?alt=media)
* Add and connect.\
  \
  Congratulations. You successfully deployed your AWS EC2 Instance aka AWS VPS.

### 4. Elastic IP Allocation

* To have a static IP, go to\
  Network & Security -> Elastic IPs -> Allocate Elastic IP
* Change nothing and click "Allocate"

![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-6fd41e3645e060f9e7e0ba5ac416dd7434718251%2Fimage.png?alt=media)

* A new window will show up. Click actions and select:

![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-1f996783461dd689fdbf3088165090c050198527%2Fimage.png?alt=media)

* Now a new window will show up to select the instance to be allocated:\
  ![](https://2207041657-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FAWUMKqE6lUPprI84aqd8%2Fuploads%2Fgit-blob-07f29c8a975fe345a15f1b01220bf054b7d5583d%2Fimage.png?alt=media)
* Select your newly created instance and Associate.

Congratulations, now you have got your static IP. Your IP address will not be changed until you release the Elastic IP.


# Navigating Through Congestion: Understanding Transaction Confirmation and Performance in Solana

In the rapidly evolving world of blockchain technology, understanding the intricacies of transaction processing is crucial for developers and users alike. Solana, known for its high-speed and high-throughput capabilities, presents a unique ecosystem where the roles of validators and RPC (Remote Procedure Call) nodes are central to the network's efficiency. This article delves into the mechanics of transaction confirmation in Solana, the impact of network congestion on its performance, and the interplay between RPC nodes and validators in maintaining the fluidity of the network.\
\
In Solana, transactions are confirmed through a unique process that involves validators and RPC (Remote Procedure Call) nodes. A transaction in Solana comprises a message and a list of signatures, with the message containing instructions, a list of accounts to load, and a recent blockhash. The blockhash plays a crucial role in the transaction lifecycle, acting as a timestamp thanks to Solana's Proof of History (PoH) mechanism. Validators are responsible for producing blocks and validating transactions, using PoH to establish a trusted sequence of events. When a user sends a transaction, it is forwarded to the current block producer, which then validates and commits the transaction to the blockchain. The recent blockhash in the transaction ensures it is processed within a specific time frame, preventing double processing and guaranteeing that transactions are current.

RPC servers in Solana serve as intermediaries between clients (like wallets or applications) and the validator network. They facilitate the submission of transactions to the blockchain and provide clients with necessary information, such as the latest blockhashes, account data, and transaction statuses. When a client submits a transaction, it's first sent to an RPC server, which then forwards it to validators. The transaction's confirmation is reliant on the validators' ability to include it in a block within the lifespan of the blockhash. Validators check if the transaction's blockhash is recent enough (within 151 slots, or approximately 60-90 seconds); if so, the transaction is processed. This efficient yet robust design allows Solana to handle a high throughput of transactions, maintaining its status as a high-performance blockchain.

During times of network congestion, Solana's performance can be impacted, affecting both RPC nodes and validators. Congestion typically occurs when the network experiences a high volume of transactions, exceeding what the validators can process in a timely manner. This surge can lead to delays in transaction processing and increased time for confirmations. Validators, responsible for producing and confirming blocks, may struggle to keep up with the influx, potentially leading to a backlog of transactions. The recent blockhash mechanism exacerbates this issue, as transactions might expire before they can be processed, requiring users to resubmit them. Additionally, validators have to manage a balance between processing new transactions and maintaining the ledger's integrity, which can be challenging during peak loads.

RPC nodes, while not directly involved in transaction validation, are significantly affected by congestion. They act as the gateway for users and applications to interact with the blockchain, handling requests for transaction submissions, data queries, and more. During periods of high traffic, RPC nodes can become overwhelmed with requests, leading to slower response times and increased latency. While RPC nodes can implement measures like rate limiting, caching, and load balancing to manage the influx of requests, they cannot resolve congestion issues within the blockchain itself. The performance of RPC nodes is inherently tied to the state of the network and the efficiency of validators. Therefore, while RPC nodes can optimize the handling of client requests and improve user experience to some extent, they cannot independently resolve the underlying issues of network congestion on the Solana blockchain. The solution to congestion largely relies on the network's scalability and validators' capacity to process transactions efficiently.

\
Solana's innovative approach to transaction processing, characterized by its Proof of History mechanism and the pivotal roles of validators and RPC nodes, showcases the blockchain's capacity for handling a significant volume of transactions. However, network congestion remains a challenge that can affect transaction confirmation times and overall network performance. While RPC nodes play a vital role in managing user interactions and requests, they are not a panacea for congestion issues inherent in the blockchain. Addressing these challenges requires a concerted effort to enhance network scalability and optimize validator efficiency. As Solana continues to evolve, understanding these dynamics becomes essential for users and developers navigating this cutting-edge blockchain landscape.


