Skip to main content

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;
}
build
function
required
Generated function that serializes arguments and builds the instruction
accounts
AccountMeta[]
required
Account metadata for resolution
errors
ErrorMetadata[]
Program error definitions for parsing
programId
string
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
options
ExecuteOptions
Execution options

ExecuteOptions

wallet
WalletAdapter
required
Wallet adapter for signing transactions
accounts
Record<string, string>
User-provided account addresses (base58)
confirmationLevel
ConfirmationLevel
Transaction confirmation levelDefault: 'confirmed'
timeout
number
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;
}
signature
string
Transaction signature (base58)
confirmationLevel
ConfirmationLevel
Achieved confirmation level
slot
number
Slot when transaction was processed
error
string
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.

AccountMeta

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}`);
  }
}

formatProgramError()

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

  1. Use confirmation levels appropriately - 'confirmed' for most cases, 'finalized' for high-value transactions
  2. Handle errors gracefully - Parse and display program errors to users
  3. Provide accounts explicitly - Pass all required accounts via options.accounts
  4. Cache PDA factories - Create PDA factories once and reuse them
  5. Set reasonable timeouts - Adjust based on network conditions
  6. 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

Build docs developers (and LLMs) love