solana_kit_instruction_plans 0.2.1
solana_kit_instruction_plans: ^0.2.1 copied to clipboard
Instruction planning for the Solana Kit Dart SDK.
solana_kit_instruction_plans #
Plan, organize, and execute complex multi-instruction and multi-transaction operations on Solana.
This is the Dart port of @solana/instruction-plans from the Solana TypeScript SDK.
Installation #
dependencies:
solana_kit_instruction_plans:
Since this package is part of the solana_kit workspace, you can also use the umbrella package:
dependencies:
solana_kit:
Overview #
Instruction plans describe operations that may go beyond a single instruction and can span multiple transactions. They define a tree of instructions with constraints on execution order:
- Sequential -- Instructions that must run in order.
- Parallel -- Instructions that can run concurrently.
- Single -- A leaf node wrapping one instruction.
- MessagePacker -- A dynamic node that packs instructions into transaction messages to fill available space.
The workflow has three stages:
- Instruction Plan -- Describe what needs to happen.
- Transaction Planner -- Convert instruction plans into transaction plans (decides how instructions are grouped into transactions).
- Transaction Plan Executor -- Compile, sign, send, and report results.
Documentation #
- Package page: https://pub.dev/packages/solana_kit_instruction_plans
- API reference: https://pub.dev/documentation/solana_kit_instruction_plans/latest/
Usage #
Creating instruction plans #
import 'dart:typed_data';
import 'package:solana_kit_addresses/solana_kit_addresses.dart';
import 'package:solana_kit_instructions/solana_kit_instructions.dart';
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
final systemProgram = Address('11111111111111111111111111111111');
final alice = Address('mpngsFd4tmbUfzDYJayjKZwZcaR7aWb2793J6grLsGu');
final bob = Address('GDhQoTpNbYUaCAjFKBMCPCdrCaKaLiSqhx5M7r4SrEFy');
final depositFromAlice = singleInstructionPlan(Instruction(
programAddress: systemProgram,
accounts: [
AccountMeta(address: alice, role: AccountRole.writableSigner),
],
data: Uint8List.fromList([2, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0]),
));
final depositFromBob = singleInstructionPlan(Instruction(
programAddress: systemProgram,
accounts: [
AccountMeta(address: bob, role: AccountRole.writableSigner),
],
data: Uint8List.fromList([2, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0]),
));
final activateVault = singleInstructionPlan(Instruction(
programAddress: systemProgram,
));
final withdrawToAlice = singleInstructionPlan(Instruction(
programAddress: systemProgram,
accounts: [
AccountMeta(address: alice, role: AccountRole.writable),
],
));
final withdrawToBob = singleInstructionPlan(Instruction(
programAddress: systemProgram,
accounts: [
AccountMeta(address: bob, role: AccountRole.writable),
],
));
Composing plans #
Combine instruction plans using sequential and parallel composition:
// Both deposits can happen in parallel, then activate, then both
// withdrawals can happen in parallel.
final plan = sequentialInstructionPlan([
parallelInstructionPlan([depositFromAlice, depositFromBob]),
activateVault,
parallelInstructionPlan([withdrawToAlice, withdrawToBob]),
]);
You can pass Instruction objects directly to the factory functions -- they are automatically wrapped in SingleInstructionPlan:
final simplePlan = sequentialInstructionPlan([
Instruction(programAddress: systemProgram),
Instruction(programAddress: systemProgram),
]);
Non-divisible plans #
When instructions must execute atomically (in a single transaction or bundle), use a non-divisible sequential plan:
final atomicPlan = nonDivisibleSequentialInstructionPlan([
Instruction(programAddress: systemProgram),
Instruction(programAddress: systemProgram),
]);
print(atomicPlan.divisible); // false
Message packer plans #
For operations that need to write large amounts of data across multiple transactions, use message packer plans. They dynamically fill each transaction to capacity:
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
// Pack data linearly, writing `totalLength` bytes across multiple
// transactions.
final linearPlan = getLinearMessagePackerInstructionPlan(
getInstruction: (offset, length) => Instruction(
programAddress: Address('11111111111111111111111111111111'),
// Build your instruction using the offset and length.
),
totalLength: 50000, // 50 KB of data to write.
);
// Pack a pre-built list of instructions, filling each transaction.
final listPlan = getMessagePackerInstructionPlanFromInstructions([
Instruction(programAddress: Address('11111111111111111111111111111111')),
Instruction(programAddress: Address('11111111111111111111111111111111')),
Instruction(programAddress: Address('11111111111111111111111111111111')),
]);
// Pack realloc instructions in chunks of 10,240 bytes.
final reallocPlan = getReallocMessagePackerInstructionPlan(
getInstruction: (size) => Instruction(
programAddress: Address('11111111111111111111111111111111'),
// Build a realloc instruction for `size` bytes.
),
totalSize: 40960, // 40 KB to reallocate.
);
Planning transactions #
A TransactionPlanner converts an instruction plan into a TransactionPlan -- a tree of transaction messages ready to be compiled and sent:
import 'package:solana_kit_addresses/solana_kit_addresses.dart';
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
import 'package:solana_kit_transaction_messages/solana_kit_transaction_messages.dart';
final planner = createTransactionPlanner(
TransactionPlannerConfig(
createTransactionMessage: () async {
// Create a new transaction message with fee payer and lifetime.
return TransactionMessage(
version: TransactionVersion.v0,
feePayer: Address('mpngsFd4tmbUfzDYJayjKZwZcaR7aWb2793J6grLsGu'),
lifetimeConstraint: BlockhashLifetimeConstraint(
blockhash: 'EkSnNWid2cvwEVnVx9aBqawnmiCNiDgp3gUdkDPTKN1N',
lastValidBlockHeight: BigInt.from(300000),
),
);
},
),
);
// Convert the instruction plan to a transaction plan.
final transactionPlan = await planner(plan);
Transaction plans #
Transaction plans mirror the instruction plan tree structure, but contain TransactionMessage objects instead of instructions:
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
import 'package:solana_kit_transaction_messages/solana_kit_transaction_messages.dart';
// Create transaction plans directly.
final msg = TransactionMessage(version: TransactionVersion.v0);
final singlePlan = singleTransactionPlan(msg);
final seqPlan = sequentialTransactionPlan([singlePlan, singlePlan]);
final parPlan = parallelTransactionPlan([singlePlan, singlePlan]);
final nonDivPlan = nonDivisibleSequentialTransactionPlan([
singlePlan,
singlePlan,
]);
Traversing transaction plan trees #
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
void main() {
// Flatten a plan tree into a list of SingleTransactionPlans.
final allSinglePlans = flattenTransactionPlan(transactionPlan);
print('Total transactions: ${allSinglePlans.length}');
// Find the first plan matching a predicate.
final found = findTransactionPlan(
transactionPlan,
(p) => p is SingleTransactionPlan,
);
// Check if all plans match a predicate.
final allSingle = everyTransactionPlan(
transactionPlan,
(p) => p is SingleTransactionPlan,
);
// Transform plans (bottom-up).
final transformed = transformTransactionPlan(
transactionPlan,
(p) => p, // identity transform
);
}
Executing transaction plans #
A TransactionPlanExecutor traverses the transaction plan tree, compiling, signing, and sending each transaction:
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
import 'package:solana_kit_transaction_messages/solana_kit_transaction_messages.dart';
final executor = createTransactionPlanExecutor(
TransactionPlanExecutorConfig(
executeTransactionMessage: (context, message) async {
// Compile, sign, and send the transaction message.
// Return a Signature string or a Transaction object.
// The `context` map can be used to store intermediate data.
return 'signature-base58-string';
},
),
);
// Execute the transaction plan.
final result = await executor(transactionPlan);
Inspecting execution results #
The execution returns a TransactionPlanResult tree that mirrors the plan structure:
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
void inspectResults(TransactionPlanResult result) {
// Summarize results into successful, failed, and canceled lists.
final summary = summarizeTransactionPlanResult(result);
print(summary.successful); // true if all succeeded
print(summary.successfulTransactions); // List of successful results
print(summary.failedTransactions); // List of failed results
print(summary.canceledTransactions); // List of canceled results
// Check if all results are successful.
print(isSuccessfulTransactionPlanResult(result));
// Flatten results into a list.
final allResults = flattenTransactionPlanResult(result);
print('Total results: ${allResults.length}');
}
Appending instruction plans to transaction messages #
You can directly append all instructions from an instruction plan to a transaction message:
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
import 'package:solana_kit_transaction_messages/solana_kit_transaction_messages.dart';
void main() {
final message = TransactionMessage(version: TransactionVersion.v0);
final updatedMessage = appendTransactionMessageInstructionPlan(
plan,
message,
);
print(updatedMessage.instructions.length);
}
Parsing flexible inputs #
The package provides helpers that accept Instruction, InstructionPlan, TransactionMessage, or TransactionPlan objects and automatically wrap them:
import 'package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart';
import 'package:solana_kit_instructions/solana_kit_instructions.dart';
final instruction = Instruction(
programAddress: Address('11111111111111111111111111111111'),
);
// Automatically wraps in SingleInstructionPlan.
final parsed = parseInstructionPlanInput(instruction);
print(parsed is SingleInstructionPlan); // true
// A list is wrapped in a SequentialInstructionPlan.
final parsedList = parseInstructionPlanInput([instruction, instruction]);
print(parsedList is SequentialInstructionPlan); // true
API Reference #
Sealed classes #
-
InstructionPlan-- The base sealed class for instruction plans.SingleInstructionPlan-- Wraps a singleInstruction.SequentialInstructionPlan-- Contains orderedplanswith adivisibleflag.ParallelInstructionPlan-- Containsplansthat can execute concurrently.MessagePackerInstructionPlan-- Provides agetMessagePackerfactory for dynamic packing.
-
TransactionPlan-- The base sealed class for transaction plans.SingleTransactionPlan-- Wraps a singleTransactionMessage.SequentialTransactionPlan-- Contains orderedplanswith adivisibleflag.ParallelTransactionPlan-- Containsplansthat can execute concurrently.
-
TransactionPlanResult-- The base sealed class for execution results.SingleTransactionPlanResult-- Base for single transaction results.SuccessfulSingleTransactionPlanResult-- Containssignature.FailedSingleTransactionPlanResult-- Containserror.CanceledSingleTransactionPlanResult-- Canceled due to earlier failure.
SequentialTransactionPlanResult-- Containsplansresults list.ParallelTransactionPlanResult-- Containsplansresults list.
Classes #
MessagePacker-- Returned byMessagePackerInstructionPlan.getMessagePacker(), withdone()andpackMessageToCapacity(message).TransactionPlannerConfig-- Configuration withcreateTransactionMessageand optionalonTransactionMessageUpdated.TransactionPlanExecutorConfig-- Configuration withexecuteTransactionMessage.TransactionPlanResultSummary-- Summary withsuccessful,successfulTransactions,failedTransactions,canceledTransactions.
Type aliases #
TransactionPlanner--Future<TransactionPlan> Function(InstructionPlan).TransactionPlanExecutor--Future<TransactionPlanResult> Function(TransactionPlan).CreateTransactionMessage--Future<TransactionMessage> Function().ExecuteTransactionMessage--Future<Object> Function(Map<String, Object?>, TransactionMessage).
Factory functions #
| Function | Description |
|---|---|
singleInstructionPlan |
Wraps an Instruction in a SingleInstructionPlan. |
sequentialInstructionPlan |
Creates a divisible sequential plan from a list. |
nonDivisibleSequentialInstructionPlan |
Creates a non-divisible sequential plan. |
parallelInstructionPlan |
Creates a parallel plan from a list. |
singleTransactionPlan |
Wraps a TransactionMessage in a SingleTransactionPlan. |
sequentialTransactionPlan |
Creates a divisible sequential transaction plan. |
nonDivisibleSequentialTransactionPlan |
Creates a non-divisible sequential transaction plan. |
parallelTransactionPlan |
Creates a parallel transaction plan. |
Tree helpers #
| Function | Description |
|---|---|
flattenInstructionPlan |
Flattens an instruction plan tree into leaf plans. |
findInstructionPlan |
Depth-first search for the first matching plan. |
everyInstructionPlan |
Returns true if all plans satisfy a predicate. |
transformInstructionPlan |
Bottom-up transformation of the plan tree. |
flattenTransactionPlan |
Flattens a transaction plan tree into SingleTransactionPlan list. |
findTransactionPlan |
Depth-first search for the first matching transaction plan. |
everyTransactionPlan |
Returns true if all transaction plans satisfy a predicate. |
transformTransactionPlan |
Bottom-up transformation of the transaction plan tree. |
flattenTransactionPlanResult |
Flattens results into SingleTransactionPlanResult list. |
findTransactionPlanResult |
Depth-first search for the first matching result. |
everyTransactionPlanResult |
Returns true if all results satisfy a predicate. |
transformTransactionPlanResult |
Bottom-up transformation of the result tree. |
Message packer factories #
| Function | Description |
|---|---|
getLinearMessagePackerInstructionPlan |
Packs data linearly with offset/length across transactions. |
getMessagePackerInstructionPlanFromInstructions |
Packs a list of instructions, filling each transaction. |
getReallocMessagePackerInstructionPlan |
Packs realloc instructions in 10,240-byte chunks. |
Planner and executor #
| Function | Description |
|---|---|
createTransactionPlanner |
Creates a TransactionPlanner from config. |
createTransactionPlanExecutor |
Creates a TransactionPlanExecutor from config. |
passthroughFailedTransactionPlanExecution |
Catches executor errors and returns the result instead of throwing. |
summarizeTransactionPlanResult |
Categorizes results into successful, failed, and canceled. |
getFirstFailedSingleTransactionPlanResult |
Finds the first failed result in a tree. |
Other functions #
| Function | Description |
|---|---|
appendTransactionMessageInstructionPlan |
Appends all instructions from a plan to a transaction message. |
parseInstructionPlanInput |
Parses flexible input into an InstructionPlan. |
parseTransactionPlanInput |
Parses flexible input into a TransactionPlan. |
Example #
Use example/main.dart as a runnable starting point for solana_kit_instruction_plans.
- Import path:
package:solana_kit_instruction_plans/solana_kit_instruction_plans.dart - This section is centrally maintained with
mdtto keep package guidance aligned. - After updating shared docs templates, run
docs:updatefrom the repo root.
Maintenance #
- Validate docs in CI and locally with
docs:check. - Keep examples focused on one workflow and reference package README sections for deeper API details.