npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@streamflow/distributor

v11.3.0

Published

JavaScript SDK to interact with Streamflow Airdrop protocol.

Readme

JS SDK to interact with Streamflow Airdrop.

This package allows you to create, claim, clawback a Token Distributor.

Token Distributor essentially that allows you to Airdrop tokens to multiple (thousands or even millions) of recipients with constant fees for the Sender. Recipient will pay gas fees when claiming tokens.

You can also use getClaims and getDistributors to fetch active claims and distributors respectively.

Table of Contents


Installation

npm i -s @streamflow/common @streamflow/distributor

or

yarn add @streamflow/common @streamflow/distributor

Documentation

More Documentation available here: docs site →

Public API documentation: API docs →

Automated Airdrop Creation: notion doc →

Import SDK

Most common imports:

import { ICluster } from "@streamflow/common";
import { SolanaDistributorClient } from "@streamflow/distributor/solana";

Check the SDK for other types and utility functions.

Create DistributorClient instance

Solana

const client = new SolanaDistributorClient({
  clusterUrl: "https://api.mainnet-beta.solana.com",
  cluster: ICluster.Mainnet,
});

Create an Airdrop (Distributor account)

const now = Math.floor(Date.now() / 1000);

const createData = {
    mint: "Gssm3vfi8s65R31SBdmQRq6cKeYojGgup7whkw4VCiQj", // mint
    name: "My Airdrop", // airdrop name
    file: csvFile, // CSV file, File | Blob
}

/* The CSV file should have the following format (headers can be omitted)
  pubkey,amount_unlocked,amount_locked,category
  1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM,0,1,Staker
  1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh,0,1,Staker
  11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3,0,1,Staker
  11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP,0,1,Staker

  Where:
  - amount_unlocked - amount of tokens unlocked right at the start of vesting (cliff);
  - amount_locked - amount to be distributed;
  - category - set to `Staker` as constant, it’s not used right now;

  Amounts are in raw format - you need to account for decimal places -> 1.5 tokens with 6 decimals will be 1500000
*/ 

const solanaParams = {
    invoker: wallet, // SignerWalletAdapter or Keypair of Sender account
    isNative: // [optional] [WILL CREATE A wSOL Airdrop] Needed only if you need to Airdrop Solana native token
};

const distributorParams = {
    mint: "Gssm3vfi8s65R31SBdmQRq6cKeYojGgup7whkw4VCiQj", // mint
    unlockPeriod: 1, // Unlock period in seconds
    startVestingTs: 0, // Timestamp when Airdrop starts, 0 for immediately upon creation
    endVestingTs: now + 3600 * 24 * 7, // Timestamp when Airdrop ends
    clawbackStartTs: now + 5, // Timestamp after which Airdrop can be clawed back to the Sender address
    claimsClosableByAdmin: false, // Whether individual Claims can be closed by the Sender
    claimsClosableByClaimant: false, // Whether the Recipient can close their own Claim
    claimsLimit: null, // The number of times a Recipient can Claim the Airdrop - 0 or null for no limit on claims
};

/* You can use our public API for generating a merkleTree or use your own implementation
  The public API is accessed with an auth token passed in in the x-api-key header of the request
  The tokens are provided manually by Streamflow by reaching out to us with the 
  wallet address that will create the Airdrop
*/
const { data: merkleResponse } = await httpClient.post(
  "https://api-public.streamflow.finance/v2/api/airdrops/",
  createData,
  headers: { 
    "Content-Type": "multipart/form-data",
    "x-api-key": "<your-auth-token>"
    }
)

// You can prepare the instructions for more granular control of execution
const { ixs } = await client.prepareCreateInstructions(
  {
    ...merkleResponse,
    root: merkleResponse.merkleRoot,
    maxNumNodes: new BN(merkleResponse.maxNumNodes).toString(),
    maxTotalClaim: new BN(merkleResponse.maxTotalClaim),
    ...distributorData,
  },
  solanaParams,
);

// Or you can call the create function which will trigger the transaction execution for you
const res = await client.create(
  {
    ...merkleResponse,
    root: merkleResponse.merkleRoot,
    maxNumNodes: new BN(merkleResponse.maxNumNodes).toString(),
    maxTotalClaim: new BN(merkleResponse.maxTotalClaim),
    ...distributorData,
  },
  solanaParams,
);

Claim an Airdrop

const solanaParams = {
    invoker: recipient, // SignerWalletAdapter or Keypair of Recipient account
};

const claimantAddress = "s3pWmY359mDrNRnDBZ3v5TrrqqxvxiW2t4U2WZyxRoA" // Wallet that will perform the claim
const ditributorAddress = res.metadataId; // address of the distributor account

// Use our public api or your own solution
const claimantData = await httpClient.get(
  `https://api-public.streamflow.finance/v2/api/airdrops/${distributorAddress}/claimants/${claimantAddress}`,
  headers: { "x-api-key": "<your-auth-token>" }
)

const claimantParams = {
    id: claimantData.distributorAddress,
    proof: claimantData.proof, // Merkle Proof used to verify claim
    amountUnlocked: new BN(claimantData.amountUnlocked), // Total amount unlocked for a Recipient
    amountLocked: new BN(claimantData.amountLocked), // Total amount locked for a Recipient
},

// Prepare the claim instructions
const { ixs } = client.prepareClaimInstructions(
    claimantParams,
    solanaParams,
),

// Or use the SDK to trigger the transaction
const claimRes = await client.claim(
    claimantParams,
    solanaParams,
);

Close a Claim

const solanaParams = {
    invoker: recipient, // SignerWalletAdapter or Keypair of Recipient account
};

// By Admin of the Airdrop, no need for proof in this case
const closeRes = await client.closeClaim(
  {
    id: res.metadataId, // address of the Distributor Account
    claimant: "s3pWmY359mDrNRnDBZ3v5TrrqqxvxiW2t4U2WZyxRoA" // address of the Recipient/Claimant
  },
  solanaParams,
);



// By Claimant
const claimantAddress = "s3pWmY359mDrNRnDBZ3v5TrrqqxvxiW2t4U2WZyxRoA" // Wallet that will perform the claim
const ditributorAddress = res.metadataId; // address of the distributor account

// Use our public api or your own solution
const claimantData = await httpClient.get(
  `https://api-public.streamflow.finance/v2/api/airdrops/${distributorAddress}/claimants/${claimantAddress}`,
  headers: { "x-api-key": "<your-auth-token>" }
)

const closeRes = await client.closeClaim(
  {
    id: claimantData.distributorAddress,
    proof: claimantData.proof, // Merkle Proof used to verify claim
    amountUnlocked: new BN(claimantData.amountUnlocked), // Total amount unlocked for a Recipient
    amountLocked: new BN(claimantData.amountLocked), // Total amount locked for a Recipient
    claimant: claimantAddress // address of the Recipient/Claimant
  },
  solanaParams,
);

Clawback an Airdrop

Returns all funds to the original Sender.

  • only Sender can clawback funds before Airdrop has ended;
  • anyone can clawback funds a day after Airdrop has ended - funds will still go to the original Sender;
const solanaParams = {
    invoker: sender, // SignerWalletAdapter or Keypair of Invoker account
};
const clawbackRes = await client.clawback({ id: res.metadataId }, solanaParams);

Search Airdrops

As all the data is stored on-chain you can also search Airdrops with searchDistributors method:

// All parameters are optional, so in theory you can just fetch all Distributors
const params = {
    mint: "BZLbGTNCSFfoth2GYDtwr7e4imWzpR5jqcUuGEwr646K", 
    admin: "s3pWmY359mDrNRnDBZ3v5TrrqqxvxiW2t4U2WZyxRoA"
};
// Return an Array of objects {publicKey: PublicKey, account: Distributor}
const disributors = await client.searchDistributors(params);

Additional notes

Streamflow Distributor protocol program IDs

| Solana | | | ------- | -------------------------------------------- | | Devnet | MErKy6nZVoVAkryxAejJz2juifQ4ArgLgHmaJCQkU7N | | Mainnet | MErKy6nZVoVAkryxAejJz2juifQ4ArgLgHmaJCQkU7N |