| name | stream-creation |
| description | Use when creating token locks or vesting schedules with @streamflow/stream. Apply when user calls createLock, createVesting, createLockBatch, createVestingBatch, or their builder variants, or when building ICreateLinearStreamData / ICreateMultipleLinearStreamData params for stream creation. |
Stream Creation
Composable API wrappers over SolanaStreamClient in packages/stream/solana/api/. Pass intent-specific params — lock/vesting defaults are pre-filled and enforced.
Choose the right function
| Goal | Function | Returns |
|---|
| Lock tokens until one date | createLock | CreateInstructionResult |
| Lock — multiple recipients | createLockBatch | BatchInstructionResult |
| Linear vesting | createVesting (no initialAllocation) | CreateInstructionResult |
| Vesting + upfront release | createVesting (with initialAllocation) | BatchInstructionResult |
| Vesting — multiple recipients | createVestingBatch | BatchInstructionResult |
import { createLock, createLockBatch, createVesting, createVestingBatch } from "@streamflow/stream/solana/api";
import type { ICreateLockParams, ICreateLockBatchParams, ICreateVestingParams, ICreateVestingBatchParams } from "@streamflow/stream/solana/api";
createLock
await createLock(
{
recipient: "Wallet...", tokenId: "Mint...",
amount: new BN(1_000_000_000),
unlockDate: 1735689600,
name: "Team Lock",
transferableByRecipient: false,
},
invoker, env,
);
Hardcoded internals — never set these on input: period=1, cliffAmount=amount-1, amountPerPeriod=BN(1), all cancel/topup/withdrawal flags false. Required for isTokenLock() classification.
createVesting
Return type is overloaded: omit initialAllocation → CreateInstructionResult; include it → BatchInstructionResult (2 streams created atomically).
await createVesting(
{
recipient, tokenId, name,
amount: new BN(1_000_000_000_000),
start: 1700000000,
period: 86400,
duration: 365 * 86400,
cliffAmount: new BN(0),
cancelableBySender: false,
automaticWithdrawal: false,
transferableBySender: false, transferableByRecipient: false,
},
invoker, env,
);
Duration rules: exactly one of duration / endDate; must be positive; must be >= period.
amountPerPeriod auto-compute (divCeil):
remaining = amount - cliffAmount
periods = floor(duration / period)
result = ceil(remaining / periods)
Classification gotcha: cliffAmount close to amount (within 1) causes the stream to classify as Lock, not Vesting.
createLockBatch / createVestingBatch
Per-recipient amount and name; shared config at top level.
await createLockBatch(
{ tokenId, unlockDate, recipients: [{ recipient, amount, name }] },
invoker, env,
);
await createVestingBatch(
{ tokenId, start, period, duration,
recipients: [{ recipient, amount, name, cliffAmount?, amountPerPeriod? }] },
invoker, env,
);
- Lock batch: each recipient auto-gets
cliffAmount = r.amount - 1, amountPerPeriod = BN(1)
- Vesting batch:
amountPerPeriod computed independently per recipient; explicit r.amountPerPeriod skips auto-compute
- Batch vesting does not support
initialAllocation
Builder variants (params only, no execution)
Use when you need the raw params object to inspect or compose with other instructions:
import { buildLockParams, buildVestingParams, buildLockBatchParams, buildVestingBatchParams, resolveDuration, computeAmountPerPeriod } from "@streamflow/stream/solana/api";
const data: ICreateLinearStreamData = buildLockParams({ ... });
const data: ICreateLinearStreamData = buildVestingParams({ ... });
const data: ICreateMultipleLinearStreamData = buildLockBatchParams({ ... });
const data: ICreateMultipleLinearStreamData = buildVestingBatchParams({ ... });
Source: packages/stream/solana/api/create-lock.ts, create-vesting.ts, create-lock-batch.ts, create-vesting-batch.ts
Advanced: protocol-level patterns
Why the defaults are what they are, isTokenLock() classification criteria, initial allocation internals, and dynamic/price-triggered stream differences: assets/advanced.md