Non-custodial payment splitting protocol for Solana. Automatically distribute incoming payments to multiple recipients based on pre-configured percentages.
Program ID: SPL1T3rERcu6P6dyBiG7K8LUr21CssZqDAszwANzNMB
Cascade Splits enables trustless payment distribution on Solana:
- Non-custodial: Funds held in PDA-owned vaults, no private keys
- Automatic: Split funds instantly on execution
- Permissionless: Anyone can trigger distributions
- Self-healing: Missing recipient ATAs handled gracefully with automatic recovery
Payment → Vault (PDA) → execute_split() → Recipients
- Authority creates a split config with recipients and percentages
- Protocol creates a vault (ATA owned by PDA) to receive payments
- Payments sent to vault
- Anyone calls
execute_split()to distribute funds
- 1-20 recipients per split configuration
- 1% protocol fee (transparent, on-chain enforced)
- SPL Token & Token-2022 support, including sRFC-37 frozen accounts
- Multiple configs per authority/mint via unique identifiers
- Idempotent execution - safe to retry
- Self-healing - unclaimed funds auto-deliver when ATAs created
- Zero-copy accounts - optimized for high-throughput micropayments
# Clone repository
git clone https://github.com/cascade-protocol/splits.git
cd splits
# Install dependencies
pnpm install
# Build program
anchor build# Mollusk unit tests (fast)
cargo test -p cascade-splits
# Anchor integration tests
anchor testcargo bench -p cascade-splitsCreating a split config requires rent-exempt deposits that are fully refundable when closing:
| Account | Size | Rent |
|---|---|---|
| SplitConfig | 1,832 bytes | 0.0136 SOL |
| Vault ATA | 165 bytes | 0.0020 SOL |
| Total | ~0.0157 SOL |
At $150/SOL ≈ $2.35 (refunded on close_split_config)
| Instruction | CUs | Notes |
|---|---|---|
| execute_split (1 recipient) | ~28,505 | Minimal case |
| execute_split (5 recipients) | ~68,573 | Typical case |
| execute_split (10 recipients) | ~109,000 | High activity |
| create_split_config | 36,590 - 40,024 | Includes vault ATA creation |
| update_split_config | 7,424 - 14,032 | Varies by recipient count |
| close_split_config | ~10,168 | Includes vault ATA closure |
| initialize_protocol | ~8,998 | One-time setup |
Scaling: ~8K CU per recipient (6K Token CPI + 2K overhead). Even 10-recipient splits use only 8% of Solana's 1.4M CU budget.
For comparison: typical DEX swaps use 100,000-400,000+ CUs.
See docs/benchmarks/compute_units.md for full benchmark history and additional scenarios (unclaimed flows, protocol admin ops).
import { createSolanaRpc } from "@solana/kit";
import { executeSplit, isCascadeSplit } from "@cascade-fyi/splits-sdk";
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
// Check if destination is a split vault
if (await isCascadeSplit(rpc, vault)) {
const result = await executeSplit(rpc, vault, executor);
if (result.ok) {
// Build and send transaction using your preferred method
const tx = buildTransaction([result.instruction], signer);
await sendTransaction(tx);
} else {
console.log(`Cannot execute: ${result.reason}`);
}
}Note: The core module returns instructions — transaction building is your responsibility, enabling compatibility with any @solana/kit version. For high-level convenience with WebSocket confirmation, use
executeAndConfirmSplitfrom@cascade-fyi/splits-sdk.
import { createSplitsClient } from "@cascade-fyi/splits-sdk";
import { createSolanaRpc, createSolanaRpcSubscriptions, createKeyPairSignerFromBytes } from "@solana/kit";
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
const rpcSubscriptions = createSolanaRpcSubscriptions("wss://api.mainnet-beta.solana.com");
const signer = await createKeyPairSignerFromBytes(secretKey);
const splits = createSplitsClient({ rpc, rpcSubscriptions, signer });
const result = await splits.ensureSplit({
recipients: [
{ address: "Agent111111111111111111111111111111111111111", share: 90 },
{ address: "Marketplace1111111111111111111111111111111", share: 10 },
],
});
// result.vault is your payment address (use as x402 payTo)See SDK documentation for complete API reference.
ProtocolConfig (global singleton)
- Authority and fee wallet configuration
- Seeds:
[b"protocol_config"]
SplitConfig (per-split)
- Recipients, percentages, vault address
- Seeds:
[b"split_config", authority, mint, unique_id] - Zero-copy for optimal compute (1,832 bytes fixed)
| Instruction | Authorization | Description |
|---|---|---|
initialize_protocol |
Deployer | One-time protocol setup |
update_protocol_config |
Protocol authority | Update fee wallet |
create_split_config |
Anyone | Create new split |
execute_split |
Anyone | Distribute vault funds |
update_split_config |
Config authority | Update recipients |
close_split_config |
Config authority | Delete config, reclaim rent |
If a recipient's ATA is missing during execution:
- Their share is recorded as "unclaimed" and stays in vault
- Funds are protected from re-splitting
- On next
execute_split, system attempts to clear unclaimed - Once recipient creates ATA, funds auto-deliver
No separate claim instruction needed - single interface for all operations.
Cascade Splits integrates seamlessly with x402 payment facilitators:
import { isCascadeSplit, executeSplit } from "@cascade-fyi/splits-sdk";
// In your facilitator's settle handler:
if (await isCascadeSplit(rpc, paymentDestination)) {
// It's a split vault - execute distribution after payment
const result = await executeSplit(rpc, paymentDestination, executor);
if (result.ok) {
// Build and send using your kit version
await sendTransaction([result.instruction], signer);
}
}Use the splitConfig address (PDA) as your payTo destination — facilitators derive the vault ATA automatically. This makes payTo token-agnostic: same address works for USDC, USDT, or any supported token. The SDK caches detection results for high-volume efficiency.
See specification for complete integration guide.
├── programs/cascade-splits/
│ ├── src/
│ │ ├── instructions/ # Instruction handlers
│ │ ├── state.rs # Account structures
│ │ ├── errors.rs # Error codes
│ │ └── events.rs # Event definitions
│ ├── tests/ # Mollusk unit tests
│ └── benches/ # Compute unit benchmarks
├── packages/splits-sdk/ # TypeScript SDK (Solana)
├── packages/splits-sdk-evm/ # TypeScript SDK (EVM)
├── apps/dashboard/ # Dashboard app
├── apps/docs/ # Documentation site
└── docs/
└── specification.md # Full specification
- Anchor 0.32.1
- Solana SDK 2.2
- Mollusk SVM 0.5.1 (testing)
- @solana/kit ^5.0.0 (SDK)
# Build program
anchor build
# Build SDK
pnpm build- Full Specification - Detailed protocol documentation
- SDK Documentation - TypeScript SDK reference
- Error Codes - All error codes and descriptions
- Events - Event definitions for indexing
- Non-custodial (PDA-owned vaults)
- Overflow/underflow checks (all math uses
checked_*) - Duplicate recipient validation
- Bounded account size (max 20 recipients)
- Protocol fee enforcement (cannot be bypassed)
- ATA validation on creation
Coming soon
Apache License 2.0 - see LICENSE file.
- GitHub: https://github.com/cascade-protocol/splits
- SDK: @cascade-fyi/splits-sdk
- Verification: OtterSec
- Contact: hello@cascade.fyi
Built by Cascade Labs