This package allows you to create
, createMultiple
, withdraw
, cancel
, topup
, transfer
, update
a token stream. You can also getOne
stream and get
multiple streams.
Installation
npm i -s @streamflow/stream
or
yarn add @streamflow/stream
Documentation
API Documentation available here: docs site โ
Import SDK
Most common imports:
Copy
import { BN } from "bn.js";
import { Types, GenericStreamClient, getBN, getNumberFromBN } from "@streamflow/stream";
Check the SDK for other types and utility functions.
Create StreamClient instance
Before creating and manipulating streams, a chain-specific or generic StreamClient
instance must be created. All stream functions are methods in this instance.
Solana
import {
StreamflowSolana,
Types,
} from "@streamflow/stream";
const solanaClient = new StreamflowSolana.SolanaStreamClient(
"https://api.mainnet-beta.solana.com"
);
Aptos
import {
StreamflowAptos,
Types,
} from "@streamflow/stream";
const aptosClient = new StreamflowAptos.AptosStreamClient(
"https://fullnode.mainnet.aptoslabs.com/v1"
);
Ethereum
import {
StreamflowEVM,
Types,
} from "@streamflow/stream";
const ethereumClient = new StreamflowEVM.EvmStreamClient(
"YOUR_ETHEREUM_NODE_URL",
Types.IChain.Ethereum,
signer // will be sender in a stream and authority for all stream related transactions
);
Polygon
import {
StreamflowEVM,
Types,
} from "@streamflow/stream";
const polygonClient = new StreamflowEVM.EvmStreamClient(
"YOUR_POLYGON_NODE_URL",
Types.IChain.Polygon,
signer // will be sender in a stream and authority for all stream related transactions
);
BNB Smart Chain
import {
StreamflowEVM,
Types,
} from "@streamflow/stream";
const bnbClient = new StreamflowEVM.EvmStreamClient(
"https://bsc-dataseed1.binance.org/",
Types.IChain.BNB,
signer // will be sender in a stream and authority for all stream related transactions
);
Generic Stream Client
GenericStreamClient
provides an isomorphic interface to work with streams agnostic of the chain.
Copy
import { GenericStreamClient, Types } from "@streamflow/stream";
const client =
new GenericStreamClient<Types.IChain.Solana>({
chain: Types.IChain.Solana, // Blockchain
clusterUrl: "https://api.mainnet-beta.solana.com", // RPC cluster URL
cluster: Types.ICluster.Mainnet, // (optional) (default: Mainnet)
// ...rest chain specific params e.g. commitment for Solana
});
Create stream
Creating a stream requires a wallet with tokens for gas fees.
Copy
const createStreamParams: Types.ICreateStreamData = {
recipient: "4ih00075bKjVg000000tLdk4w42NyG3Mv0000dc0M00", // Recipient address.
tokenId: "DNw99999M7e24g99999999WJirKeZ5fQc6KY999999gK", // Token mint address.
start: 1643363040, // Timestamp (in seconds) when the stream/token vesting starts.
amount: getBN(100, 9), // depositing 100 tokens with 9 decimals mint.
period: 1, // Time step (period) in seconds per which the unlocking occurs.
cliff: 1643363160, // Vesting contract "cliff" timestamp in seconds.
cliffAmount: new BN(10), // Amount unlocked at the "cliff" timestamp.
amountPerPeriod: getBN(5, 9), // Release rate: how many tokens are unlocked per each period.
name: "Transfer to Jane Doe.", // The stream name or subject.
canTopup: false, // setting to FALSE will effectively create a vesting contract.
cancelableBySender: true, // Whether or not sender can cancel the stream.
cancelableByRecipient: false, // Whether or not recipient can cancel the stream.
transferableBySender: true, // Whether or not sender can transfer the stream.
transferableByRecipient: false, // Whether or not recipient can transfer the stream.
automaticWithdrawal: true, // Whether or not a 3rd party (e.g. cron job, "cranker") can initiate a token withdraw/transfer.
withdrawalFrequency: 10, // Relevant when automatic withdrawal is enabled. If greater than 0 our withdrawor will take care of withdrawals. If equal to 0 our withdrawor will skip, but everyone else can initiate withdrawals.
partner: null, // (optional) Partner's wallet address (string | null).
};
const solanaParams = {
sender: wallet, // SignerWalletAdapter or Keypair of Sender account
isNative: // [optional] [WILL CREATE A wSOL STREAM] Wether Stream or Vesting should be paid with Solana native token or not
};
const aptosParams = {
senderWallet: wallet, // AptosWalletAdapter Wallet of sender
};
const ethereumParams = undefined;
try {
const { ixs, tx, metadata } = await client.create(createStreamParams, solanaParams); // second argument differ depending on a chain
} catch (exception) {
// handle exception
}
Protocol | a program that lives on chain, an implementation of Scheduled Token Transfer |
Stream | an entity that is created by the Protocol, contains information about Token Transfer |
Contract | a synonym for a Stream. In the code there is no consistency how we name it, in the Protocol we often use |
Sender | an account that creates a Scheduled Token Transfer |
Recipient | an account that is the receiver of Tokens |
Referral | an account that will be used to calculate streamflow and referral fees when creating a Stream |
Partner | a synonym for a Referral |
Treasury | a Streamflow account that receives fees as a reward |
Withdrawor | a Streamflow account that processes automated Token Transfers to the Recipient |
Protocol | a program that lives on chain, an implementation of Scheduled Token Transfer |
Stream | an entity that is created by the Protocol, contains information about Token Transfer |
Create multiple streams at once
const recipients = [
{
recipient: "4ih00075bKjVg000000tLdk4w42NyG3Mv0000dc0M00", // Solana recipient address.
amount: getBN(100, 9), // depositing 100 tokens with 9 decimals mint.
name: "January Payroll", // The stream name/subject.
cliffAmount: getBN(10, 9), // amount released on cliff for this recipient
amountPerPeriod: getBN(1, 9), //amount released every specified period epoch
},
];
const createStreamParams: Types.ICreateMultipleStreamData = {
recipients: recipients, // Solana recipient address.
tokenId: "DNw99999M7e24g99999999WJirKeZ5fQc6KY999999gK", // SPL Token mint.
start: 1643363040, // Timestamp (in seconds) when the stream/token vesting starts.
period: 1, // Time step (period) in seconds per which the unlocking occurs.
cliff: 1643363160, // Vesting contract "cliff" timestamp in seconds.
canTopup: false, // setting to FALSE will effectively create a vesting contract.
cancelableBySender: true, // Whether or not sender can cancel the stream.
cancelableByRecipient: false, // Whether or not recipient can cancel the stream.
transferableBySender: true, // Whether or not sender can transfer the stream.
transferableByRecipient: false, // Whether or not recipient can transfer the stream.
automaticWithdrawal: true, // Whether or not a 3rd party (e.g. cron job, "cranker") can initiate a token withdraw/transfer.
withdrawalFrequency: 10, // Relevant when automatic withdrawal is enabled. If greater than 0 our withdrawor will take care of withdrawals. If equal to 0 our withdrawor will skip, but everyone else can initiate withdrawals.
partner: null, // (optional) Partner's wallet address (string | null).
};
const solanaParams = {
sender: wallet, // SignerWalletAdapter or Keypair of Sender account
isNative: // [optional] [WILL CREATE A wSOL STREAM] Wether Stream or Vesting should be paid with Solana native token or not
};
const aptosParams = {
senderWallet: wallet, // AptosWalletAdapter Wallet of sender
};
const ethereumParams = undefined;
try {
const { txs } = await client.createMultiple(createMultiStreamsParams, solanaParams);
} catch (exception) {
// handle exception
}
Identifying created contracts (streams or vesting)
All Stream Clients return Types.ICreateResult
object (createdMultiple
returns an Array) that has the following structure
Copy
interface ICreateResult {
ixs: (TransactionInstruction | Types.TransactionPayload)[];
txId: string;
metadataId: MetadataId;
}
metadataId
is the id of the created stream.
Interacting with existing streams
Withdraw from stream
const withdrawStreamParams: Types.IWithdrawData = {
id: "AAAAyotqTZZMAAAAmsD1JAgksT8NVAAAASfrGB5RAAAA", // Identifier of a stream to be withdrawn from.
amount: getBN(100, 9), // Requested amount to withdraw. If stream is completed, the whole amount will be withdrawn.
};
const solanaParams = {
invoker: wallet, // SignerWalletAdapter or Keypair signing the transaction
};
const aptosParams = {
senderWallet: wallet, // AptosWalletAdapter Wallet of wallet signing the transaction
tokenId: "0x1::aptos_coin::AptosCoin", // Aptos Coin type
};
const ethereumParams = undefined;
try {
const { ixs, tx } = await client.withdraw(withdrawStreamParams, solanaParams);
} catch (exception) {
// handle exception
}
Topup stream
Topping up a stream allows you to add funds to an existing contract and extend the duration of the stream/vesting.
const topupStreamParams: Types.ITopUpData = {
id: "AAAAyotqTZZMAAAAmsD1JAgksT8NVAAAASfrGB5RAAAA", // Identifier of a stream to be topped up.
amount: getBN(100, 9), // Specified amount to topup (increases deposited amount).
};
const solanaParams = {
invoker: wallet, // SignerWalletAdapter or Keypair signing the transaction
isNative: // [ONLY FOR wSOL STREAMS] [optional] Wether topup is with Native Solanas
};
const aptosParams = {
senderWallet: wallet, // AptosWalletAdapter Wallet of wallet signing the transaction
tokenId: "0x1::aptos_coin::AptosCoin", // Aptos Coin type
};
const ethereumParams = undefined;
try {
const { ixs, tx } = await client.topup(topupStreamParams, solanaParams);
} catch (exception) {
// handle exception
}
Transfer stream
Transfer stream to another recipient
const data: Types.ITransferData = {
id: "AAAAyotqTZZMAAAAmsD1JAgksT8NVAAAASfrGB5RAAAA",
newRecipient: "99h00075bKjVg000000tLdk4w42NyG3Mv0000dc0M99", // Identifier of a stream to be transferred.
};
const solanaParams = {
invoker: wallet, // SignerWalletAdapter or Keypair signing the transaction
};
const aptosParams = {
senderWallet: wallet, // AptosWalletAdapter Wallet of wallet signing the transaction
tokenId: "0x1::aptos_coin::AptosCoin", // Aptos Coin type
};
const ethereumParams = undefined;
try {
const { tx } = await client.transfer(data, solanaParams);
} catch (exception) {
// handle exception
}
Cancel stream
const cancelStreamParams: Types.ICancelData = {
id: "AAAAyotqTZZMAAAAmsD1JAgksT8NVAAAASfrGB5RAAAA", // Identifier of a stream to be canceled.
};
const solanaParams = {
invoker: wallet, // SignerWalletAdapter or Keypair signing the transaction
};
const aptosParams = {
senderWallet: wallet, // AptosWalletAdapter Wallet of wallet signing the transaction
tokenId: "0x1::aptos_coin::AptosCoin", // Aptos Coin type
};
const ethereumParams = undefined;
try {
const { ixs, tx } = await StreamClient.cancel(cancelStreamParams, solanaParams);
} catch (exception) {
// handle exception
}
Get one stream by its ID
const data: Types.IGetOneData = {
id: "AAAAyotqTZZMAAAAmsD1JAgksT8NVAAAASfrGB5RAAAA", // Identifier of a stream
};
try {
const stream = await client.getOne(data);
} catch (exception) {
// handle exception
}
Get multiple streams for a specific wallet address
const data: Types.IGetAllData = {
address: "99h00075bKjVg000000tLdk4w42NyG3Mv0000dc0M99",
type: Types.StreamType.All,
direction: Types.StreamDirection.All,
};
try {
const streams = client.get(data);
} catch (exception) {
// handle exception
}
Fetching unlocked amount
const stream = await client.getOne({
id: "AAAAyotqTZZMAAAAmsD1JAgksT8NVAAAASfrGB5RAAAA",
});
const unlocked = stream.unlocked(tsInSeconds); // bn amount unlocked at the tsInSeconds
console.log(getNumberFromBN(unlocked, 9));
Note: unlocked amount is determined based on configuration set on creation, no dynamic data is involved.
Reading withdrawn amount and remaining funds
const stream = await client.getOne({
id: "AAAAyotqTZZMAAAAmsD1JAgksT8NVAAAASfrGB5RAAAA",
});
const withdrawn = stream.withdrawnAmount; // bn amount withdrawn already
console.log(getNumberFromBN(wihtdrawn, 9));
const remaining = stream.remaining(9); // amount of remaining funds
console.log(remaining);
Please note that transaction fees for the scheduled transfers are paid upfront by the stream creator (sender).
Our Github: https://github.com/streamflow-finance
To see some examples of what you can build with Streamflow, check out: https://streamflow.finance/integrations.
See also:
Something we didn't cover?
We've tried to cover as much as possible in this guide, but there is always room for improvement. If we missed something, or you'd like to simply share your ideas, love, and support, email us at [email protected]
Someone will be in touch with you in no time. โ๏ธ