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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion packages/kit-plugin-rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,83 @@
[npm-image]: https://img.shields.io/npm/v/@solana/kit-plugin-rpc.svg?style=flat&label=%40solana%2Fkit-plugin-rpc
[npm-url]: https://www.npmjs.com/package/@solana/kit-plugin-rpc

TODO: Document
This package provides plugins that add RPC functionality to your Kit clients.

## Installation

```sh
pnpm install @solana/kit-plugin-rpc
```

> [!NOTE]
> This package is included in the main [`@solana/kit-plugins`](../kit-plugins) package.
>
> ```sh
> pnpm install @solana/kit-plugins
> ```

## `rpc` plugin

The RPC plugin adds `rpc` and `rpcSubscriptions` objects to your Kit client, allowing you to call RPC methods and subscribe to RPC notifications.

### Installation

To use the `rpc` plugin, you must provide the URL of your desired Solana RPC endpoint.

```ts
import { createEmptyClient } from '@solana/kit';
import { rpc } from '@solana/kit-plugins';

const client = createEmptyClient().use(rpc('https://api.mainnet-beta.solana.com'));
```

Note that you may wrap your RPC URL using the `mainnet`, `devnet`, or `testnet` helpers from `@solana/kit`. When you do, the returned RPC API will be adjusted to match the selected cluster since some RPC features are not available on all clusters.

```ts
import { mainnet } from '@solana/kit';

const client = createEmptyClient().use(rpc(mainnet('https://api.mainnet-beta.solana.com')));
```

By default, the WebSocket URL is derived from the RPC's HTTP URL but you may configure it explicitly using the second parameter. This config object can also be used to customize other aspects of RPC Subscriptions behavior.

```ts
const client = createEmptyClient().use(
rpc('https://my-rpc-url.com', {
url: 'wss://my-rpc-ws-url.com',
minChannels: 5,
maxSubscriptionsPerChannel: 50,
}),
);
```

### Features

- `rpc`: Call any Solana RPC method using type-safe methods.
```ts
const { value: latestBlockhash } = await client.rpc.getLatestBlockhash().send();
```
- `rpcSubscriptions`: Subscribe to Solana RPC notifications using async iterators.
```ts
const slotNotifications = await client.rpcSubscriptions.slotNotifications({ commitment: 'confirmed' }).subscribe();
for await (const slotNotification of slotNotifications) {
console.log('Got a slot notification', slotNotification);
}
```

## `localhostRpc` plugin

This plugin is an alias for the `rpc` plugin pre-configured to connect to a local Solana validator.

### Installation

```ts
import { createEmptyClient } from '@solana/kit';
import { localhostRpc } from '@solana/kit-plugins';

const client = createEmptyClient().use(localhostRpc());
```

### Features

_See the `rpc` plugin for available features_.
5 changes: 3 additions & 2 deletions packages/kit-plugin-rpc/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@solana/kit-plugin-rpc",
"version": "0.1.0",
"description": "TODO",
"description": "RPC support for Kit clients",
"exports": {
"types": "./dist/types/index.d.ts",
"react-native": "./dist/index.react-native.mjs",
Expand Down Expand Up @@ -48,7 +48,8 @@
"@solana/kit": "^5.1.0"
},
"devDependencies": {
"@solana/kit": "^5.1.0"
"@solana/kit": "^5.1.0",
"@solana/plugin-core": "workspace:*"
},
"license": "MIT",
"repository": {
Expand Down
71 changes: 70 additions & 1 deletion packages/kit-plugin-rpc/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,70 @@
export function rpcPlugin() {}
import {
ClusterUrl,
createSolanaRpc,
createSolanaRpcSubscriptions,
DefaultRpcSubscriptionsChannelConfig,
} from '@solana/kit';

/**
* Enhances a client with Solana RPC and RPC Subscriptions capabilities.
*
* @param url - The URL of the Solana cluster.
* @param rpcSubscriptionsConfig - Optional configuration for RPC subscriptions.
*
* @example
* ```ts
* import { createEmptyClient } from '@solana/kit';
* import { rpc } from '@solana/kit-plugins';
*
* // Install the RPC plugin.
* const client = createEmptyClient().use(rpc('https://api.mainnet-beta.solana.com'));
*
* // Make RPC calls.
* const { value: latestBlockhash } = await client.rpc.getLatestBlockhash().send();
*
* // Subscribe to RPC notifications.
* const slotNotifications = await client.rpcSubscriptions.slotNotifications({ commitment: 'confirmed' }).subscribe();
* for await (const slotNotification of slotNotifications) {
* console.log('Got a slot notification', slotNotification);
* }
* ```
*
* @see {@link localhostRpc}
*/
export function rpc<TClusterUrl extends ClusterUrl>(
url: TClusterUrl,
rpcSubscriptionsConfig?: DefaultRpcSubscriptionsChannelConfig<TClusterUrl>,
) {
const rpc = createSolanaRpc(url);
const rpcSubscriptionsUrl = rpcSubscriptionsConfig?.url ?? url.replace(/^http/, 'ws');
const rpcSubscriptions = createSolanaRpcSubscriptions(rpcSubscriptionsUrl, rpcSubscriptionsConfig);
return <T extends object>(client: T) => ({ ...client, rpc, rpcSubscriptions });
}

/**
* Enhances a client with Solana RPC and RPC Subscriptions capabilities
* using to a local validator.
*
* @example
* ```ts
* import { createEmptyClient } from '@solana/kit';
* import { rpc } from '@solana/kit-plugins';
*
* // Install the Localhost RPC plugin.
* const client = createEmptyClient().use(localhostRpc());
*
* // Make RPC calls.
* const { value: latestBlockhash } = await client.rpc.getLatestBlockhash().send();
*
* // Subscribe to RPC notifications.
* const slotNotifications = await client.rpcSubscriptions.slotNotifications({ commitment: 'confirmed' }).subscribe();
* for await (const slotNotification of slotNotifications) {
* console.log('Got a slot notification', slotNotification);
* }
* ```
*
* @see {@link rpc}
*/
export function localhostRpc() {
return rpc('http://127.0.0.1:8899', { url: 'ws://127.0.0.1:8900' });
}
35 changes: 35 additions & 0 deletions packages/kit-plugin-rpc/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { mainnet } from '@solana/kit';
import { createEmptyClient } from '@solana/plugin-core';
import { describe, expect, expectTypeOf, it } from 'vitest';

import { localhostRpc, rpc } from '../src';

describe('rpc', () => {
it('initializes the rpc and rpcSubscriptions properties', () => {
const client = createEmptyClient().use(rpc('https://api.mainnet-beta.solana.com'));
expect(client).toHaveProperty('rpc');
expect(client).toHaveProperty('rpcSubscriptions');
expect(client.rpc.sendTransaction).toBeTypeOf('function');
expect(client.rpcSubscriptions.accountNotifications).toBeTypeOf('function');
});

it('narrows the RPC API based on the cluster', () => {
const client = createEmptyClient().use(rpc(mainnet('https://api.mainnet-beta.solana.com')));
expectTypeOf(client.rpc).not.toHaveProperty('requestAirdrop');
});
});

describe('localhostRpc', () => {
it('initializes the rpc and rpcSubscriptions properties', () => {
const client = createEmptyClient().use(localhostRpc());
expect(client).toHaveProperty('rpc');
expect(client).toHaveProperty('rpcSubscriptions');
expect(client.rpc.sendTransaction).toBeTypeOf('function');
expect(client.rpcSubscriptions.accountNotifications).toBeTypeOf('function');
});

it('has access to airdrop methods', () => {
const client = createEmptyClient().use(localhostRpc());
expectTypeOf(client.rpc).toHaveProperty('requestAirdrop');
});
});
7 changes: 0 additions & 7 deletions packages/kit-plugin-rpc/test/noop.test.ts

This file was deleted.

3 changes: 2 additions & 1 deletion packages/kit-plugins/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"@solana/kit": "^5.1.0"
},
"dependencies": {
"@solana/kit-plugin-rpc": "workspace:*"
"@solana/kit-plugin-rpc": "workspace:*",
"@solana/plugin-core": "workspace:*"
},
"devDependencies": {
"@solana/kit": "^5.1.0"
Expand Down
6 changes: 6 additions & 0 deletions packages/kit-plugins/src/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { localhostRpc } from '@solana/kit-plugin-rpc';
import { createEmptyClient } from '@solana/plugin-core';

export function createDefaultLocalhostClient() {
return createEmptyClient().use(localhostRpc());
}
4 changes: 3 additions & 1 deletion packages/kit-plugins/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export function createDefaultClient() {}
export * from '@solana/kit-plugin-rpc';

export * from './defaults';
18 changes: 18 additions & 0 deletions packages/kit-plugins/test/defaults.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Rpc, RpcSubscriptions, SolanaRpcApi, SolanaRpcSubscriptionsApi } from '@solana/kit';
import { describe, expect, expectTypeOf, it } from 'vitest';

import { createDefaultLocalhostClient } from '../src';

describe('createDefaultLocalhostClient', () => {
it('it offers an RPC client', () => {
const client = createDefaultLocalhostClient();
expect(client.rpc).toBeTypeOf('object');
expectTypeOf(client.rpc).toEqualTypeOf<Rpc<SolanaRpcApi>>();
});

it('it offers an RPC Subscriptions client', () => {
const client = createDefaultLocalhostClient();
expect(client.rpcSubscriptions).toBeTypeOf('object');
expectTypeOf(client.rpcSubscriptions).toEqualTypeOf<RpcSubscriptions<SolanaRpcSubscriptionsApi>>();
});
});
7 changes: 0 additions & 7 deletions packages/kit-plugins/test/noop.test.ts

This file was deleted.

3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.