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
20 changes: 20 additions & 0 deletions .changeset/tame-bottles-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'@solana/kit-plugin-rpc': minor
'@solana/kit-plugin-litesvm': minor
---

Replace the `priorityFees` config option on `rpcTransactionPlanner` and `litesvmTransactionPlanner` with a new `TransactionPlannerConfig` object that supports `microLamportsPerComputeUnit` and `version`. Add a `transactionConfig` option to `solanaRpc` and `litesvm` for passing transaction planner configuration through the all-in-one plugins.

**BREAKING CHANGES**

**`priorityFees` replaced with `TransactionPlannerConfig`.** The inline `priorityFees` option has been replaced with a `TransactionPlannerConfig` object. The `priorityFees` option on `SolanaRpcConfig` has been replaced with `transactionConfig`.

```diff
- rpcTransactionPlanner({ priorityFees: 100n as MicroLamports })
+ rpcTransactionPlanner({ microLamportsPerComputeUnit: 100n as MicroLamports })
```

```diff
- solanaRpc({ url, priorityFees: 100n as MicroLamports })
+ solanaRpc({ url, transactionConfig: { microLamportsPerComputeUnit: 100n as MicroLamports } })
```
4 changes: 2 additions & 2 deletions packages/kit-client-rpc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export function createClient<TClusterUrl extends ClusterUrl>(config: ClientConfi
.use(rpc<TClusterUrl>(config.url, config.rpcSubscriptionsConfig))
.use(payer(config.payer))
.use(rpcGetMinimumBalance())
.use(rpcTransactionPlanner({ priorityFees: config.priorityFees }))
.use(rpcTransactionPlanner({ microLamportsPerComputeUnit: config.priorityFees }))
.use(rpcTransactionPlanExecutor({ maxConcurrency: config.maxConcurrency, skipPreflight: config.skipPreflight }))
.use(planAndSendTransactions());
}
Expand Down Expand Up @@ -109,7 +109,7 @@ export function createLocalClient(
.use(rpcAirdrop())
.use(rpcGetMinimumBalance())
.use(payerOrGeneratedPayer(config.payer))
.use(rpcTransactionPlanner({ priorityFees: config.priorityFees }))
.use(rpcTransactionPlanner({ microLamportsPerComputeUnit: config.priorityFees }))
.use(rpcTransactionPlanExecutor({ maxConcurrency: config.maxConcurrency, skipPreflight: config.skipPreflight }))
.use(planAndSendTransactions());
}
13 changes: 11 additions & 2 deletions packages/kit-plugin-litesvm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ import { payer } from '@solana/kit-plugin-payer';
const client = createClient().use(payer(myPayer)).use(litesvm());
```

### Options

All options are provided via a `LiteSvmConfig` object:

- `transactionConfig`: Options to configure how transaction messages are created. See `litesvmTransactionPlanner` options below.

### Features

- `svm`: Access the underlying LiteSVM instance.
Expand Down Expand Up @@ -123,7 +129,7 @@ const client = createClient().use(litesvmConnection()).use(litesvmGetMinimumBala

## `litesvmTransactionPlanner` plugin

This plugin provides a default transaction planner that creates transaction messages with a fee payer, a provisory compute unit limit, and optional priority fees.
This plugin provides a default transaction planner that creates transaction messages with a fee payer and optional priority fees.

### Installation

Expand All @@ -147,7 +153,10 @@ const client = await createClient()

### Options

- `priorityFees`: Priority fees in micro lamports per compute unit.
All options are provided via a `TransactionPlannerConfig` object:

- `version`: The transaction message version to use. Accepts `0` or `'legacy'`. Defaults to `0`.
- `microLamportsPerComputeUnit`: Priority fees in micro lamports per compute unit. Defaults to no priority fees.

### Features

Expand Down
1 change: 1 addition & 0 deletions packages/kit-plugin-litesvm/src/index.browser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type { FailedTransactionMetadata, TransactionMetadata } from 'litesvm';
export type { LiteSvmConfig } from './litesvm';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch exporting LiteSvmConfig from the browser entry point so consumers can still reference the type even when the runtime function throws.

export type { LiteSVM } from './litesvm-connection';
export type { LiteSvmRpcApi } from './litesvm-to-rpc';

Expand Down
14 changes: 11 additions & 3 deletions packages/kit-plugin-litesvm/src/litesvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import { litesvmAirdrop } from './airdrop';
import { litesvmGetMinimumBalance } from './get-minimum-balance';
import { litesvmConnection } from './litesvm-connection';
import { litesvmTransactionPlanExecutor } from './transaction-plan-executor';
import { litesvmTransactionPlanner } from './transaction-planner';
import { litesvmTransactionPlanner, TransactionPlannerConfig } from './transaction-planner';

export type LiteSvmConfig = {
/**
* Options to configure how transaction messages are created such as
* choosing a transaction version or setting priority fees.
*/
transactionConfig?: TransactionPlannerConfig;
};

/**
* Enhances a client with a full LiteSVM setup including an SVM connection,
Expand All @@ -31,14 +39,14 @@ import { litesvmTransactionPlanner } from './transaction-planner';
*
* @see {@link litesvmConnection}
*/
export function litesvm() {
export function litesvm(config: LiteSvmConfig = {}) {
return <T extends ClientWithPayer>(client: T) => {
return pipe(
client,
litesvmConnection(),
litesvmAirdrop(),
litesvmGetMinimumBalance(),
litesvmTransactionPlanner(),
litesvmTransactionPlanner(config.transactionConfig),
litesvmTransactionPlanExecutor(),
planAndSendTransactions(),
);
Expand Down
40 changes: 29 additions & 11 deletions packages/kit-plugin-litesvm/src/transaction-planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,44 @@ import {
* .use(litesvmTransactionPlanExecutor());
* ```
*/
export function litesvmTransactionPlanner(
config: {
/**
* The priority fees to be set on the transaction in micro lamports per compute unit.
* Defaults to using no priority fees.
*/
priorityFees?: MicroLamports;
} = {},
) {
export function litesvmTransactionPlanner(config: TransactionPlannerConfig = {}) {
return <T extends ClientWithPayer>(client: T) => {
const transactionPlanner = createTransactionPlanner({
createTransactionMessage: () => {
return pipe(
createTransactionMessage({ version: 0 }),
createTransactionMessage({ version: config.version ?? 0 }),
tx => setTransactionMessageFeePayerSigner(client.payer, tx),
tx => (config.priorityFees ? setTransactionMessageComputeUnitPrice(config.priorityFees, tx) : tx),
tx =>
config.microLamportsPerComputeUnit
? setTransactionMessageComputeUnitPrice(config.microLamportsPerComputeUnit, tx)
: tx,
);
},
});

return extendClient(client, { transactionPlanner });
};
}

/**
* Configuration options for the transaction planner.
*
* The `version` field is used to determine the transaction version
* to use when creating transaction messages and determines the shape
* of the rest of the configuration options, as some options are only
* applicable to certain transaction versions.
*/
// TODO(loris): Add support for v1 transactions when LiteSVM supports them.
// This includes: `version: 1` and `priorityFees?: Lamports` in a new union variant.
export type TransactionPlannerConfig = {
/**
* The priority fees to be set on the transaction in micro lamports per compute unit.
* Defaults to using no priority fees.
*/
microLamportsPerComputeUnit?: MicroLamports;
/**
* The transaction message version to use when creating transaction messages.
* Defaults to version 0.
*/
version?: 'legacy' | 0;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: since this type is identical to the one in kit-plugin-rpc/src/transaction-planner.ts, would it make sense to extract it into a shared package (e.g. kit-plugin-instruction-plan or a new shared types package) to avoid the manual sync + the explicit disambiguation in kit-plugins/src/index.ts? Not blocking — the TODO comments and barrel re-export handle it fine for now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that on purpose to allow the two config objects to grow independently of each other.

49 changes: 49 additions & 0 deletions packages/kit-plugin-litesvm/test/transaction-planner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import {
Address,
createClient,
generateKeyPairSigner,
getTransactionMessageComputeUnitPrice,
MicroLamports,
singleInstructionPlan,
SingleTransactionPlan,
TransactionMessage,
TransactionSigner,
} from '@solana/kit';
import { describe, expect, it } from 'vitest';
Expand Down Expand Up @@ -35,6 +38,52 @@ describe('litesvmTransactionPlanner', () => {
expect(transactionPlan.message.feePayer).toBe(payer);
});

it('creates version 0 transaction messages by default', async () => {
const payer = await generateKeyPairSigner();
const client = createClient()
.use(() => ({ payer }))
.use(litesvmTransactionPlanner());

const instructionPlan = singleInstructionPlan(MOCK_INSTRUCTION);
const transactionPlan = (await client.transactionPlanner(instructionPlan)) as SingleTransactionPlan;
expect(transactionPlan.message.version).toBe(0);
});

it('creates legacy transaction messages when configured', async () => {
const payer = await generateKeyPairSigner();
const client = createClient()
.use(() => ({ payer }))
.use(litesvmTransactionPlanner({ version: 'legacy' }));

const instructionPlan = singleInstructionPlan(MOCK_INSTRUCTION);
const transactionPlan = (await client.transactionPlanner(instructionPlan)) as SingleTransactionPlan;
expect(transactionPlan.message.version).toBe('legacy');
});

it('does not set a compute unit price by default', async () => {
const payer = await generateKeyPairSigner();
const client = createClient()
.use(() => ({ payer }))
.use(litesvmTransactionPlanner());

const instructionPlan = singleInstructionPlan(MOCK_INSTRUCTION);
const transactionPlan = (await client.transactionPlanner(instructionPlan)) as SingleTransactionPlan;
const message = transactionPlan.message as TransactionMessage & { version: 'legacy' | 0 };
expect(getTransactionMessageComputeUnitPrice(message)).toBeUndefined();
});

it('sets a compute unit price when configured', async () => {
const payer = await generateKeyPairSigner();
const client = createClient()
.use(() => ({ payer }))
.use(litesvmTransactionPlanner({ microLamportsPerComputeUnit: 100n as MicroLamports }));

const instructionPlan = singleInstructionPlan(MOCK_INSTRUCTION);
const transactionPlan = (await client.transactionPlanner(instructionPlan)) as SingleTransactionPlan;
const message = transactionPlan.message as TransactionMessage & { version: 'legacy' | 0 };
expect(getTransactionMessageComputeUnitPrice(message)).toBe(100n);
});

it('requires a payer on the client', () => {
// @ts-expect-error TypeScript fails but we don't throw an error at runtime.
expect(() => createClient().use(litesvmTransactionPlanner())).not.toThrow();
Expand Down
7 changes: 5 additions & 2 deletions packages/kit-plugin-rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ All options are provided via a `SolanaRpcConfig` object:
- `rpcSubscriptionsUrl`: URL of the RPC Subscriptions endpoint. Defaults to the `rpcUrl` with the protocol changed from `http` to `ws`.
- `rpcConfig`: Optional configuration forwarded to `createSolanaRpc`.
- `rpcSubscriptionsConfig`: Optional configuration forwarded to `createSolanaRpcSubscriptions`.
- `priorityFees`: Priority fees in micro-lamports per compute unit. Defaults to no priority fees.
- `transactionConfig`: Options to configure how transaction messages are created. See `rpcTransactionPlanner` options below.
- `maxConcurrency`: Maximum number of concurrent transaction executions. Defaults to 10.
- `skipPreflight`: Whether to always skip preflight simulation. Defaults to `false`.

Expand Down Expand Up @@ -301,7 +301,10 @@ const client = await createClient()

### Options

- `priorityFees`: Priority fees in micro lamports per compute unit.
All options are provided via a `TransactionPlannerConfig` object:

- `version`: The transaction message version to use. Accepts `0` or `'legacy'`. Defaults to `0`.
- `microLamportsPerComputeUnit`: Priority fees in micro lamports per compute unit. Defaults to no priority fees.

### Features

Expand Down
15 changes: 7 additions & 8 deletions packages/kit-plugin-rpc/src/solana-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
DevnetUrl,
extendClient,
MainnetUrl,
MicroLamports,
pipe,
} from '@solana/kit';
import { planAndSendTransactions } from '@solana/kit-plugin-instruction-plan';
Expand All @@ -16,7 +15,7 @@ import { rpcAirdrop } from './airdrop';
import { rpcGetMinimumBalance } from './get-minimum-balance';
import { rpcConnection, rpcSubscriptionsConnection } from './rpc';
import { rpcTransactionPlanExecutor } from './transaction-plan-executor';
import { rpcTransactionPlanner } from './transaction-planner';
import { rpcTransactionPlanner, TransactionPlannerConfig } from './transaction-planner';

/**
* Configuration for the Solana RPC plugins.
Expand All @@ -29,11 +28,6 @@ export type SolanaRpcConfig<TClusterUrl extends ClusterUrl = ClusterUrl> = {
* Defaults to 10.
*/
maxConcurrency?: number;
/**
* The priority fees to set on transactions in micro-lamports per compute unit.
* Defaults to no priority fees.
*/
priorityFees?: MicroLamports;
/** Optional configuration forwarded to {@link createSolanaRpc}. */
rpcConfig?: Parameters<typeof createSolanaRpc>[1];
/** Optional configuration forwarded to {@link createSolanaRpcSubscriptions}. */
Expand All @@ -57,6 +51,11 @@ export type SolanaRpcConfig<TClusterUrl extends ClusterUrl = ClusterUrl> = {
* Defaults to `false`.
*/
skipPreflight?: boolean;
/**
* Options to configure how transaction messages are created such as
* choosing a transaction version or setting priority fees.
*/
transactionConfig?: TransactionPlannerConfig;
};

/**
Expand Down Expand Up @@ -96,7 +95,7 @@ export function solanaRpc<TClusterUrl extends ClusterUrl>(config: SolanaRpcConfi
config.rpcSubscriptionsConfig,
),
rpcGetMinimumBalance(),
rpcTransactionPlanner({ priorityFees: config.priorityFees }),
rpcTransactionPlanner(config.transactionConfig),
rpcTransactionPlanExecutor({ maxConcurrency: config.maxConcurrency, skipPreflight: config.skipPreflight }),
planAndSendTransactions(),
);
Expand Down
40 changes: 29 additions & 11 deletions packages/kit-plugin-rpc/src/transaction-planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,45 @@ import {
* .use(rpcTransactionPlanExecutor());
* ```
*/
export function rpcTransactionPlanner(
config: {
/**
* The priority fees to be set on the transaction in micro lamports per compute unit.
* Defaults to using no priority fees.
*/
priorityFees?: MicroLamports;
} = {},
) {
export function rpcTransactionPlanner(config: TransactionPlannerConfig = {}) {
return <T extends ClientWithPayer>(client: T) => {
const transactionPlanner = createTransactionPlanner({
createTransactionMessage: () => {
return pipe(
createTransactionMessage({ version: 0 }),
createTransactionMessage({ version: config.version ?? 0 }),
tx => setTransactionMessageFeePayerSigner(client.payer, tx),
tx => fillTransactionMessageProvisoryComputeUnitLimit(tx),
tx => (config.priorityFees ? setTransactionMessageComputeUnitPrice(config.priorityFees, tx) : tx),
tx =>
config.microLamportsPerComputeUnit
? setTransactionMessageComputeUnitPrice(config.microLamportsPerComputeUnit, tx)
: tx,
);
},
});

return extendClient(client, { transactionPlanner });
};
}

/**
* Configuration options for the transaction planner.
*
* The `version` field is used to determine the transaction version
* to use when creating transaction messages and determines the shape
* of the rest of the configuration options, as some options are only
* applicable to certain transaction versions.
*/
// TODO(loris): Add support for v1 transactions when `createTransactionMessage` supports them.
// This includes: `version: 1` and `priorityFees?: Lamports` in a new union variant.
export type TransactionPlannerConfig = {
/**
* The priority fees to be set on the transaction in micro lamports per compute unit.
* Defaults to using no priority fees.
*/
microLamportsPerComputeUnit?: MicroLamports;
/**
* The transaction message version to use when creating transaction messages.
* Defaults to version 0.
*/
version?: 'legacy' | 0;
};
Loading
Loading