Documentation Index
Fetch the complete documentation index at: https://mintlify.com/hypertekorg/hyperstack/llms.txt
Use this file to discover all available pages before exploring further.
Overview
HyperStack provides a powerful instruction execution system for Solana programs with automatic:
- Account resolution (signer, PDA, user-provided)
- PDA derivation with type-safe seed definitions
- Transaction building and signing
- Confirmation waiting
- Error parsing
Quick Start
import { HyperStack } from '@hyperstack/core';
import type { WalletAdapter } from '@hyperstack/core';
const stack = {
name: 'my-program',
url: 'wss://api.example.com',
views: { /* ... */ },
instructions: {
createUser: myCreateUserHandler,
},
};
const client = await HyperStack.connect(stack);
// Execute instruction
const result = await client.instructions.createUser(
{ name: 'Alice', age: 30 },
{ wallet }
);
console.log('Transaction:', result.signature);
Instruction Handlers
Instruction handlers are generated from your Solana program IDL and define how to build and execute instructions.
InstructionHandler Interface
interface InstructionHandler {
build(args: Record<string, unknown>, accounts: ResolvedAccounts): BuiltInstruction;
accounts: AccountMeta[];
errors: ErrorMetadata[];
programId?: string;
}
Generated function that serializes arguments and builds the instruction
Account metadata for resolution
Program error definitions for parsing
Program ID used for PDA derivation
Executing Instructions
Via Client Instructions
The recommended way is to use the client’s instruction interface.
const result = await client.instructions.createUser(
{ name: 'Alice' }, // Instruction arguments
{ wallet } // Execution options
);
executeInstruction()
Direct execution function for manual control.
import { executeInstruction } from '@hyperstack/core';
const result = await executeInstruction(
handler,
args,
options
);
handler
InstructionHandler
required
Instruction handler from generated SDK
args
Record<string, unknown>
required
Instruction arguments
ExecuteOptions
Wallet adapter for signing transactions
User-provided account addresses (base58)
Transaction confirmation levelDefault: 'confirmed'
Maximum time to wait for confirmation in millisecondsDefault: 60000
refresh
Array<{ view: string; key?: string }>
Views to refresh after transaction completes
ExecutionResult
interface ExecutionResult {
signature: string;
confirmationLevel: ConfirmationLevel;
slot: number;
error?: string;
}
Transaction signature (base58)
Achieved confirmation level
Slot when transaction was processed
Error code if transaction failed
Confirmation Levels
type ConfirmationLevel = 'processed' | 'confirmed' | 'finalized';
processed - Transaction processed but not confirmed
confirmed - Transaction confirmed by cluster (recommended for most use cases)
finalized - Transaction finalized and irreversible (recommended for high-value transactions)
const result = await client.instructions.createUser(
{ name: 'Alice' },
{
wallet,
confirmationLevel: 'finalized',
timeout: 90000, // 90 seconds
}
);
Account Resolution
HyperStack automatically resolves accounts based on their category.
interface AccountMeta {
name: string;
category: AccountCategory;
isMut: boolean;
isSigner: boolean;
pda?: PdaConfig;
}
Account Categories
type AccountCategory =
| 'signer' // Resolved from wallet
| 'pda' // Derived using PDA config
| 'account' // User-provided via options.accounts
| 'literal'; // Hardcoded address
Providing Accounts
const result = await client.instructions.transferTokens(
{ amount: 100 },
{
wallet,
accounts: {
mint: 'TokenMintAddress...',
destination: 'DestinationAddress...',
},
}
);
PDA Derivation
Program Derived Addresses (PDAs) are automatically derived using seed definitions.
Creating PDAs
import { pda, literal, account, arg, bytes } from '@hyperstack/core';
// Define PDA factory
const userPda = pda(
'YourProgramID...',
literal('user'),
account('authority'),
arg('userId', 'u64')
);
// Derive address
const address = await userPda.derive({
accounts: { authority: wallet.publicKey },
args: { userId: 123 },
});
Seed Types
literal()
Static string seed.
literal('user') // UTF-8 encoded "user"
account()
Reference to an account address.
account('authority') // 32-byte public key
arg()
Reference to an instruction argument.
arg('userId', 'u64') // 8-byte unsigned integer
arg('name') // String (UTF-8 encoded)
arg('mint', 'pubkey') // 32-byte public key
Supported types:
u8, u16, u32, u64, u128 - Unsigned integers
i8, i16, i32, i64, i128 - Signed integers
pubkey - Public key (32 bytes)
- String values are UTF-8 encoded
bytes()
Raw bytes seed.
bytes(new Uint8Array([1, 2, 3, 4]))
PdaFactory
interface PdaFactory {
readonly seeds: readonly SeedDef[];
readonly programId: string;
program(programId: string): PdaFactory;
derive(context: PdaDeriveContext): Promise<string>;
deriveSync(context: PdaDeriveContext): string;
}
derive()
Asynchronously derive PDA address.
const address = await userPda.derive({
accounts: { authority: 'AuthorityAddress...' },
args: { userId: 123 },
programId: 'OverrideProgramID...', // Optional
});
deriveSync()
Synchronously derive PDA address.
const address = userPda.deriveSync({
accounts: { authority: 'AuthorityAddress...' },
args: { userId: 123 },
});
program()
Override the program ID.
const testPda = userPda.program('TestProgramID...');
Program PDAs
Organize PDAs for a program.
import { createProgramPdas, pda, literal, account, arg } from '@hyperstack/core';
const pdas = createProgramPdas({
user: pda(
'ProgramID...',
literal('user'),
account('authority')
),
userStats: pda(
'ProgramID...',
literal('user-stats'),
account('user')
),
vault: pda(
'ProgramID...',
literal('vault'),
arg('mint', 'pubkey')
),
});
// Derive addresses
const userAddress = await pdas.user.derive({
accounts: { authority: wallet.publicKey },
});
const vaultAddress = await pdas.vault.derive({
args: { mint: 'MintAddress...' },
});
Low-Level PDA Functions
findProgramAddress()
Asynchronously find a valid PDA.
import { findProgramAddress } from '@hyperstack/core';
const [address, bump] = await findProgramAddress(
[seed1, seed2, seed3],
'ProgramID...'
);
findProgramAddressSync()
Synchronously find a valid PDA.
import { findProgramAddressSync } from '@hyperstack/core';
const [address, bump] = findProgramAddressSync(
[seed1, seed2, seed3],
'ProgramID...'
);
derivePda()
Derive PDA with explicit bump seed.
import { derivePda } from '@hyperstack/core';
const address = derivePda(
[seed1, seed2],
bump,
'ProgramID...'
);
Wallet Adapters
Implement the WalletAdapter interface to integrate with any wallet.
interface WalletAdapter {
publicKey: string;
connected: boolean;
signAndSend(transaction: unknown): Promise<string>;
signTransaction?(transaction: unknown): Promise<unknown>;
signAllTransactions?(transactions: unknown[]): Promise<unknown[]>;
}
Example: Phantom Wallet
import type { WalletAdapter } from '@hyperstack/core';
class PhantomWalletAdapter implements WalletAdapter {
constructor(private phantom: any) {}
get publicKey(): string {
return this.phantom.publicKey.toBase58();
}
get connected(): boolean {
return this.phantom.isConnected;
}
async signAndSend(transaction: unknown): Promise<string> {
const { signature } = await this.phantom.signAndSendTransaction(transaction);
return signature;
}
}
const wallet = new PhantomWalletAdapter(window.solana);
Bound Executor
Create an executor bound to a specific wallet.
import { createInstructionExecutor } from '@hyperstack/core';
const executor = createInstructionExecutor(wallet);
const result = await executor.execute(
handler,
{ name: 'Alice' },
{ confirmationLevel: 'confirmed' } // No need to pass wallet
);
Error Handling
parseInstructionError()
Parse program errors from transaction failures.
import { parseInstructionError } from '@hyperstack/core';
try {
await client.instructions.createUser({ name: 'Alice' }, { wallet });
} catch (error) {
const programError = parseInstructionError(error, handler.errors);
if (programError) {
console.error(`${programError.name}: ${programError.message}`);
}
}
Format program error for display.
import { formatProgramError } from '@hyperstack/core';
const formatted = formatProgramError(programError);
console.error(formatted);
Helper Functions
Base58 Encoding
import { encodeBase58, decodeBase58 } from '@hyperstack/core';
const encoded = encodeBase58(new Uint8Array([1, 2, 3]));
const decoded = decodeBase58(encoded);
Instruction Data Serialization
import { serializeInstructionData } from '@hyperstack/core';
const data = serializeInstructionData(
discriminator,
args,
argsSchema
);
Confirmation Waiting
import { waitForConfirmation } from '@hyperstack/core';
const confirmation = await waitForConfirmation(
signature,
'confirmed',
60000
);
console.log('Confirmed at slot:', confirmation.slot);
Type Exports
import type {
// Execution
InstructionHandler,
InstructionDefinition,
ExecuteOptions,
ExecutionResult,
ConfirmationLevel,
BuiltInstruction,
// Accounts
AccountMeta,
AccountCategory,
ResolvedAccount,
ResolvedAccounts,
AccountResolutionResult,
// PDAs
PdaConfig,
PdaSeed,
SeedDef,
PdaDeriveContext,
PdaFactory,
ProgramPdas,
// Wallet
WalletAdapter,
WalletState,
WalletConnectOptions,
// Errors
ProgramError,
ErrorMetadata,
// Serialization
ArgSchema,
ArgType,
} from '@hyperstack/core';
Best Practices
- Use confirmation levels appropriately -
'confirmed' for most cases, 'finalized' for high-value transactions
- Handle errors gracefully - Parse and display program errors to users
- Provide accounts explicitly - Pass all required accounts via
options.accounts
- Cache PDA factories - Create PDA factories once and reuse them
- Set reasonable timeouts - Adjust based on network conditions
- Refresh views after mutations - Use
options.refresh to update UI automatically
Next Steps
HyperStack Client
Learn how to configure instruction handlers
Working with Views
Query data after executing instructions