Actions

Guide to using Action intents for generic Solana program interaction — DeFi, staking, NFT minting, governance, and any on-chain operation not covered by Payment or Ticket intents.

When to Use Action Intents

Action intents are for any Solana program call that doesn't transfer value (use Payment) or represent an entitlement (use Ticket). Examples:

  • Calling a Jupiter swap
  • Staking tokens in a liquid staking protocol
  • Minting an NFT
  • Voting in a DAO governance proposal
  • Interacting with a dev-owned Solana program

Data Payload

pub struct FreeFormPayload {
    pub kind: String,   // e.g. "jupiter_swap", "stake_deposit", "nft_mint"
    pub data: Vec<u8>,  // Serialized application-specific fields
}

The SDK does not parse or validate the contents of data. Your application defines the schema and the exit target performs validation.

Trust Tiers

Actions support two trust tiers:

TierBehaviorUse Case
UnsignedNo cryptographic signature; fingerprinted via content hashLow-stakes actions: move avatar, save game state
SignedEd25519 signature + per-key nonce replay protectionHigh-stakes actions: spend tokens, trade items, claim rewards

Choose the appropriate tier for each action kind. The trust tier lint system warns when high-stakes kinds are registered as Unsigned.

Creating an Action Intent

Expo SDK

import { createIntent } from '@zypp-labs/expo-sdk';

// Unsigned action (low-stakes)
const { intentId } = createIntent('move_avatar', '{"x":10,"y":20}');

// Signed action (high-stakes) — requires trust tier parameter
const { intentId: signedId } = createIntent('swap_tokens', JSON.stringify({
  fromMint: 'So11111111111111111111111111111111111111112',
  toMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  amount: 100_000_000,
}), 'Signed');

Unity SDK

// Unsigned
var intent = ZyppSdk.CreateIntent("move_avatar", "{\"x\":10}", ZyppTrustTier.Unsigned);

// Signed
var signedIntent = ZyppSdk.CreateIntent("swap_tokens", swapPayload, ZyppTrustTier.Signed);

Rust Core

use zypp_labs_core::types::*;

let action = Intent::new(
    uuid::Uuid::new_v4(),
    IntentType::Action,
    IntentPayload::Action(FreeFormPayload {
        kind: String::from("jupiter_swap"),
        data: bincode::serde::encode_to_vec(&swap_params, bincode::config::standard()).unwrap(),
    }),
    TrustTier::Signed,
    Some(crypto_envelope),
);

Settlement Routing

Action intents route to one of two exit targets:

  1. Developer Backend — your server validates and records the action
  2. Developer Solana Program — a dev-owned program, such as an ephemeral rollup or custom program
// Settle via developer backend
settle(intentId, 'https://api.myapp.com/actions', 1);

// Settle via dev Solana program
settle(intentId, 'https://rpc.devnet.solana.com', 2);

The caller must provide the exit target explicitly — there is no SDK default.

Composing Multiple Actions

Action intents can batch multiple instructions in a single intent for atomic execution:

// Multiple actions in sequence
const actions = [
  { kind: 'approve', data: { spender, amount } },
  { kind: 'swap', data: { fromMint, toMint, amount } },
  { kind: 'deposit', data: { protocol, amount } },
];

Trust Tier Lint

Register action kinds with their expected trust tier to catch misconfigurations at development time:

import { registerKind, lint } from '@zypp-labs/expo-sdk';

// Register kinds with expected trust tiers
const warnings = registerKind({
  kind: 'swap_tokens',
  intent_type: 'Action',
  trust_tier: 'Signed',
  fields: []
});

// Audit all registered kinds and queued intents
const lintWarnings = lint();
if (lintWarnings.length > 0) {
  console.warn('Trust tier issues found:', lintWarnings);
}

High-stakes action kinds matching patterns like "spend", "trade", "claim", "stake", "transfer" registered as Unsigned emit warnings at registration time.

Error Handling

ErrorCauseAction
TrustTierTooLowIntent tier below kind requirementRe-create with Signed tier
ValidationErrorBackend rejected the actionCheck backend validation rules
DuplicateInLocalQueueSame action already queuedCheck queue before retrying

Next Steps

  • Set up the Expo SDK for mobile action intents
  • Configure Settlement for your exit target
  • Browse Examples for action implementations