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

Mutations allow you to execute Solana instructions through your Hyperstack. The React SDK provides the useInstructionMutation hook for managing instruction execution state.

useInstructionMutation

A hook for executing instructions with built-in loading, error, and success states.

Signature

function useInstructionMutation(
  execute: InstructionExecutor
): UseMutationResult

Parameters

execute
InstructionExecutor
required
The instruction executor function from your stack client

Return Value

submit
(args: Record<string, unknown>, options?: UseMutationOptions) => Promise<ExecutionResult>
Function to execute the instruction with the given arguments
status
MutationStatus
Current mutation status: 'idle' | 'pending' | 'success' | 'error'
error
string | null
Error message if execution failed (includes parsed program errors)
signature
string | null
Transaction signature if execution succeeded
isLoading
boolean
Convenience boolean for status === 'pending'
reset
() => void
Reset mutation state back to idle

Basic Usage

Access instruction mutations through useHyperstack:
import { useHyperstack } from 'hyperstack-react';
import { MY_STACK } from './stack';

function MyComponent() {
  const { instructions } = useHyperstack(MY_STACK);
  const mutation = instructions.myInstruction.useMutation();
  
  const handleSubmit = async () => {
    try {
      const result = await mutation.submit({
        amount: 100,
        recipient: 'ABC123...'
      });
      
      console.log('Transaction signature:', result.signature);
    } catch (error) {
      console.error('Transaction failed:', error);
    }
  };
  
  return (
    <div>
      <button 
        onClick={handleSubmit}
        disabled={mutation.isLoading}
      >
        {mutation.isLoading ? 'Processing...' : 'Submit'}
      </button>
      
      {mutation.error && (
        <div className="error">{mutation.error}</div>
      )}
      
      {mutation.signature && (
        <div className="success">
          Success! Signature: {mutation.signature}
        </div>
      )}
    </div>
  );
}

Submit Options

The submit function accepts optional execution options:
interface UseMutationOptions {
  confirmationLevel?: 'processed' | 'confirmed' | 'finalized';
  onSuccess?: (result: ExecutionResult) => void;
  onError?: (error: Error) => void;
}

Example with Callbacks

function TransferForm() {
  const { instructions } = useHyperstack(MY_STACK);
  const mutation = instructions.transfer.useMutation();
  
  const handleTransfer = () => {
    mutation.submit(
      { amount: 100 },
      {
        confirmationLevel: 'confirmed',
        onSuccess: (result) => {
          console.log('Transfer confirmed:', result.signature);
          // Refresh relevant views or show notification
        },
        onError: (error) => {
          console.error('Transfer failed:', error);
          // Show error notification
        }
      }
    );
  };
  
  return <button onClick={handleTransfer}>Transfer</button>;
}

Error Handling

The hook automatically parses Solana program errors:
function ErrorExample() {
  const { instructions } = useHyperstack(MY_STACK);
  const mutation = instructions.myInstruction.useMutation();
  
  // mutation.error will contain parsed error like:
  // "InsufficientFunds: Not enough tokens in account"
  
  return (
    <div>
      {mutation.error && (
        <Alert type="error">
          <h4>Transaction Failed</h4>
          <p>{mutation.error}</p>
        </Alert>
      )}
    </div>
  );
}

Status States

Render different UI based on mutation status:
function StatusExample() {
  const mutation = instructions.myInstruction.useMutation();
  
  return (
    <div>
      {mutation.status === 'idle' && (
        <button onClick={() => mutation.submit({ value: 42 })}>
          Submit
        </button>
      )}
      
      {mutation.status === 'pending' && (
        <div>
          <Spinner />
          <p>Processing transaction...</p>
        </div>
      )}
      
      {mutation.status === 'success' && (
        <div>
          <CheckIcon />
          <p>Transaction successful!</p>
          <a href={`https://explorer.solana.com/tx/${mutation.signature}`}>
            View on Explorer
          </a>
        </div>
      )}
      
      {mutation.status === 'error' && (
        <div>
          <ErrorIcon />
          <p>{mutation.error}</p>
          <button onClick={mutation.reset}>Try Again</button>
        </div>
      )}
    </div>
  );
}

Resetting State

Use reset() to clear mutation state:
function ResetExample() {
  const mutation = instructions.myInstruction.useMutation();
  
  useEffect(() => {
    // Reset when component unmounts or user navigates away
    return () => mutation.reset();
  }, []);
  
  return (
    <div>
      <button onClick={() => mutation.submit({ value: 1 })}>
        Submit
      </button>
      
      {mutation.status !== 'idle' && (
        <button onClick={mutation.reset}>Reset</button>
      )}
    </div>
  );
}

Direct Execution

You can also execute instructions directly without using the mutation hook:
function DirectExecution() {
  const { instructions } = useHyperstack(MY_STACK);
  const [loading, setLoading] = useState(false);
  
  const handleExecute = async () => {
    setLoading(true);
    
    try {
      const result = await instructions.myInstruction.execute({
        value: 42
      });
      
      console.log('Signature:', result.signature);
    } catch (error) {
      console.error('Error:', error);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <button onClick={handleExecute} disabled={loading}>
      Execute
    </button>
  );
}
The useMutation() hook is recommended over direct execution for better state management and error handling.

Form Integration

Combine with form libraries like React Hook Form:
import { useForm } from 'react-hook-form';

interface FormData {
  amount: number;
  recipient: string;
}

function TransferForm() {
  const { instructions } = useHyperstack(MY_STACK);
  const mutation = instructions.transfer.useMutation();
  const { register, handleSubmit } = useForm<FormData>();
  
  const onSubmit = async (data: FormData) => {
    try {
      await mutation.submit(data);
    } catch (error) {
      // Error is stored in mutation.error
    }
  };
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input 
        type="number" 
        {...register('amount', { required: true })} 
      />
      <input 
        type="text" 
        {...register('recipient', { required: true })} 
      />
      
      <button type="submit" disabled={mutation.isLoading}>
        {mutation.isLoading ? 'Sending...' : 'Send'}
      </button>
      
      {mutation.error && <div className="error">{mutation.error}</div>}
      {mutation.signature && <div className="success">Sent!</div>}
    </form>
  );
}

Optimistic Updates

Update UI optimistically before confirmation:
function OptimisticExample() {
  const { views, instructions } = useHyperstack(MY_STACK);
  const { data: count } = views.Counter.current.use();
  const mutation = instructions.increment.useMutation();
  const [optimisticCount, setOptimisticCount] = useState<number | null>(null);
  
  const handleIncrement = async () => {
    // Optimistically update UI
    setOptimisticCount((count?.value ?? 0) + 1);
    
    try {
      await mutation.submit({});
      // Real data will arrive via WebSocket
    } catch (error) {
      // Revert optimistic update on error
      setOptimisticCount(null);
    }
  };
  
  const displayCount = optimisticCount ?? count?.value ?? 0;
  
  return (
    <div>
      <p>Count: {displayCount}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}

Next Steps

Examples

See complete working examples

Core SDK

Learn about instruction definitions

Build docs developers (and LLMs) love