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 🙏

© 2024 – Pkg Stats / Ryan Hefner

buddy.link

v0.7.7

Published

The only Buddy Link package you need if you are using Javascript/Typescript

Downloads

1,274

Readme

Buddy.Link SDK

npm version NPM License Static Badge Static Badge

The official typescript SDK for Buddy!

✨ The fast integration guide for the Buddy SDK is in the Quickstart Integration section below.

Full docs for the Buddy.Link protocol can be found at docs.buddy.link. These docs are currently being updated to match the SDK rewrite, underlying protocol concepts are the same but some code samples and patterns are still being updated.

Overview

The Buddy.Link SDK lets you add referrals and rewards to your apps and protocols incredibly fast without costly program deployments or expensive custom development. Users can generate and share referral links, referral attribution can be done invisibly or through custom referral pages, and rewards are paid out instantly to users or can be manually allocated based on on-chain and off-chain activity.

The SDK currently supports react development, and will include react-native and other support in future releases. There are also components that act as helpers or wrapper for commonly required tools in frontends deployments.

Quickstart Integration

The following steps will get your integration online as fast as possible. For additional information, check out theSDK Reference Guide section below or use the Buddy.Link Docs.

Step 1 - add the Buddy.Link package to your project

You need to install the package before you can use it. Alternatively, add "buddy.link": "^0.1.2" to your package.json dependencies.

  • npm: npm install buddy.link
  • yarn: yarn add buddy.link
  • bun: bun add buddy.link

Step 2 - add BuddyState to your app

The following should be done in the highest level of your app possible. Usually this is the same file where ConnectionProvider and WalletProvider are added.

You can use all default values when initializing BuddyState. If you want to add custom state for your app/game or override default state values check the section on Customizing initial values of BuddyState below.

//index.js or layout.js, etc

import { initBuddyState, initialBuddyLink } from "buddy.link";

// Initialize Buddy State
initBuddyState({ ...initialBuddyLink });

Step 3 - Add the BuddyLinkWrapper

This wrapper allows the current connection and wallet to be used through the app via buddyState, as well as in the SDK so that you can reduce the code used to access these on several pages in your app. This should be done in the highest level of your app possible. Usually this is the same file where ConnectionProvider and WalletProvider are added.

//index.js or layout.js, etc

import {
  ConnectionProvider, useConnection, useWallet,
  WalletProvider,
} from "@solana/wallet-adapter-react";
import { initBuddyState, initialBuddyLink, useInitBuddyLink } from "buddy.link";

// Make sure you initBuddyState before initializing the wrapper!
initBuddyState({ ...initialBuddyLink });

const InitBuddyLinkWrapper = () => {
  const { connection } = useConnection();
  const wallet = useWallet();

  useInitBuddyLink(connection, wallet, 'your_organization_string');
  return null;
};

You now have BuddyState and BuddyLink initialized for your app, and values can be accessed anywhere they are needed. The steps below are for specific actions you may want to do in your app.

Initialize a new user

This process creates all necessary accounts and treasuries for the connected wallet, and also automatically accepts a referral if a referral link was used to arrive at the current page.

The code sample below is for an Invite component to add a button to your app where users can generate their own invite link by setting up their Buddy.Link accounts and treasuries.

./components/invite.jsx

import { useBuddyState, useBuddyLink, BUDDY_STATUS } from "buddy.link"

const Invite = () => {
    const [status,] = useBuddyState(BUDDY_STATUS);

    const { init } = useBuddyLink();

    const handleInit = useCallback(async () => {
        if (!status?.init) return

        try {
        const results = await init();

        console.log(`buddy.init results`, results);
        return true;
        } catch (e) {
        console.log(`buddy.init error`, e)
        }
    }, [status]);

    return (
        <button onClick={() => handleInit()}>Create Link</button>
    );
}

export default Invite

Get a user's referral code and display a referral link

This code sample verifies that the user has a profile and displays text with the user's referral link.

./components/referralLink.jsx

import { useBuddyState, BUDDY_PROFILE } from "buddy.link"

const ShareLink = () => {
    const [profile,] = useBuddyState(BUDDY_PROFILE);

    const shareLink = useMemo(() => {
    const linkString = (profile?.account?.name
        ? `laddercaster.com?r=${profile.account.name}`
        : `laddercaster.com`);

    return linkString;
    }, [profile]);

    return(
        <p>{linkString}</p>
    );
}

export default ShareLink

Customizing the initial values of BuddyState

BuddyState is a fully built state-management solution used inside the Buddy.Link SDK. You can add additional state fields to use throughout your application, as well as override the default state settings that the Buddy.Link SDK initializes during setup.

To initialize BuddyState with different values you can either override individual states or create your own initialState file with multiple new fields and parameters. You must create an initialState file to add new state fields to your app.

//initialState.js has the defaults for BuddyState

export const initialBuddyLink = {
  [BUDDY_CONNECTION]: null,
  [BUDDY_PUBLIC_KEY]: undefined,
  [BUDDY_WALLET]: undefined,
  [BUDDY_MEMBERS]: null,
  [BUDDY_MEMBER]: null,
  [BUDDY_OPTIONS]: {},
  [BUDDY_PROFILE]: null,
  [BUDDY_STATS]: null,
  [BUDDY_TREASURIES]: null,
  [BUDDY_TREASURY_PDA]: null,
  [BUDDY_STATUS]: {},
  [BUDDY_ORGANIZATION]: null,
  [BUDDY_CLIENT]: undefined,
  [BUDDY_MINTS]: [],
  [BUDDY_LOADING]: {
    // Session
    isLoadingInit: false,

    // Current User
    isLoadingProfile: false,
    isLoadingTreasuries: false,
    isLoadingMember: false,
    isLoadingTreasuryPDA: false,

    // Current Organization
    isLoadingOrganization: false,
    isLoadingMints: false,
    isLoadingMembers: false,
    isLoadingStats: false,

    // Global
    isLoadingGlobalMembers: false,
  }
};

First, follow the regular steps for initializing BuddyState. This is the normal code pattern in the highest level of you application, usually placed inside the ConnectionProvider and WalletProvider.

import { initBuddyState, initialBuddyLink } from "buddy.link";

// Initialize Buddy State
initBuddyState({ ...initialBuddyLink });

Then, use buddyState to set the initial value. In the example below, we are overriding the value of BUDDY_MINTS to a set of mint addresses for SPL tokens that we want to use when our app users initialize treasuries. Setting two values for SOL and USDC here means that all pre-checks during user setup will make sure they have SOL and USDC treasuries already or will add them to the setup transaction the user signs when creating their accounts and getting their referral link.

import { useBuddyState } from "buddy.link";

const [mints, setMints] = useBuddyState(BUDDY_MINTS);

// Ensure this only runs once during your app's initial loading phase
setMints([
    "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",  // USDC on mainnet
    "11111111111111111111111111111111"               // SOL on mainnet
]);

That's it! Now the BUDDY_MINTS value that is referenced through the SDK will know to look for both SOL and USDC treasuries for your users.

First, add the following two files:

initialState.jsThis file allows you to override any initial states set by the SDK. As an example, add mint addresses for any SPL tokens you want users to earn and claim.

//initialState.js

// Any keys not overridden in this file can be removed from this list.
import { BUDDY_MINTS, BUDDY_CLIENT, BUDDY_MEMBERS, BUDDY_OPTIONS,
  BUDDY_ORGANIZATION, BUDDY_PROFILE, BUDDY_STATS, BUDDY_TREASURIES,
  BUDDY_PUBLIC_KEY, BUDDY_CONNECTION, BUDDY_STATUS, BUDDY_WALLET } from 'buddy.link'

// Any custom keys you add to buddyState should be added to this list.
// import {
//   TURN_DATA,
//   ACTIVE_MODAL,
//   CUSTOM_NEW_STATE_KEY
// } from "./stateIndex";

// initialState values are created from the SDK, but initial overrides can be set here. You can also add entirely new state objects here that will be accessible with BuddyState.
export const initialState = {
  [BUDDY_MINTS]: [
    "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",  // USDC on mainnet
    "11111111111111111111111111111111"               // SOL on mainnet
  ],

  // [TURN_DATA]: {"turn": 0, "season": 1},
  // [ACTIVE_MODAL]: '',
  // [CUSTOM_NEW_STATE_KEY]: null
}

stateIndex.jsThis file allows you to define new states that can be used in your App. As an example, manage values for active turns with TURN_DATA, track whether modals are active with an object called ACTIVE_MODAL, or add an entirely custom value like CUSTOM_NEW_STATE_KEY.

//stateIndex.js

export const TURN_DATA = "TURN_DATA";
export const ACTIVE_MODAL = "ACTIVE_MODAL";
export const CUSTOM_NEW_STATE_KEY = "CUSTOM_NEW_STATE_KEY";

Then, add the initialState object to the initialization arrays for BuddyState. This code should be in the highest level of your application, usually placed inside the ConnectionProvider and WalletProvider.

import { initBuddyState, initialBuddyLink } from "buddy.link";
import { initialState } from "./initialState";

// Initialize Buddy State
initBuddyState({
  ...initialBuddyLink
  ...initialState      // Note: Later values will override earlier values when merging objects with spread syntax
});

Now, initialState values will be used to automatically override the BUDDY_MINTS as well as initialize new keys in buddyState if desired.

You can access and update the new buddyState values anywhere in your app with the following code:

import { useBuddyState, BUDDY_MINTS } from "buddy.link";
import { TURN_DATA, CUSTOM_NEW_STATE_KEY } from "./stateIndex";

const [mints, setMints] = useBuddyState(BUDDY_MINTS);
const [turnData, setTurnData] = useBuddyState(TURN_DATA);
const [customNewState, setCustomNewState] = useBuddyState(CUSTOM_NEW_STATE_KEY);

// Pattern for non-object updates:
setCustomNewState("some message you want to save in buddyState");

// Pattern for object updates:
setTurnData(prevData => ({...prevData, turn: 17}));

console.log("value of BUDDY_MINTS", mints);
console.log("value of TURN_DATA", turnData);
console.log("value of CUSTOM_NEW_STATE_KEY", customNewState);

Using the Status and Loading objects in your App

The Buddy.Link SDK manages two very important objects in BuddyState. You can use status and loading to handle all conditional logic around data fetching and account setup sequences in your apps.

  • status is a quick way to check if accounts, organizations, and other on-chain values exist without having to access each one via BuddyState.
  • loading allows you to see which values are currently being fetched from chain after event triggers such as wallet or connection account changing, dependent values updating, or refreshes and resets from both frontend and the SDK.
status: {
// 	
  init: true //initClient has run, all buddyState values should be updated

// Current User Status
  hasProfile: true //current wallet has global profile
  hasTreasuries: true //current wallet has treasuries
  hasMember: true //current wallet has member for current organization
  hasTreasuryPDA: true //current wallet has treasury for primary mint for current organization

// Current Organization Status
  hasOrganization: true //current organization exists
  hasMints: true //current organization has mints
  hasMembers: true //current organization has members
  hasStats: true //loaded stats for members

// Global Buddy Status
  hasGlobalMembers: true //loaded global member count for every organization on buddylink protocol
}
loading: {
    // Session
    isLoadingInit: false,

    // Current User
    isLoadingProfile: false,
    isLoadingTreasuries: false,
    isLoadingMember: false,
    isLoadingTreasuryPDA: false,

    // Current Organization
    isLoadingOrganization: false,
    isLoadingMints: false,
    isLoadingMembers: false,
    isLoadingStats: false,

    // Global
    isLoadingGlobalMembers: false,
  }

A short example of using status and loading to handle app display is shown below. For larger examples, check out the Buddy.Link Examples Repo on github.

import { useBuddyState, BUDDY_STATUS, BUDDY_LOADING, BUDDY_WALLET } from "buddy.link";

const [wallet] = useBuddyState(BUDDY_WALLET);
const [status] = useBuddyState(BUDDY_STATUS);
const [loading] = useBuddyState(BUDDY_LOADING);

// if wallet connected and accessible via BuddyState, display connected publicKey
// if SDK is still initializing, display a placeholder message.
// if the current wallet's Profile or Member values are loading, display a loading message
// if both are loaded, prompt users without a profile or member to generate their own link (initializes those accounts), otherwise display their referral link

return(
  <div>
    wallet && wallet.publicKey && <p>`current connected wallet is ${wallet?.publicKey?.toBase58() || '(error getting publicKey)'}</p>
    <div>
      {loading.isLoadingInit ? (
        <p>Buddy.Link SDK is still loading</p>
      ) : (
        {(loading.isLoadingProfile || loading.isLoadingMember) ? (
          <p>Fetching your accounts from chain...</p>
        ) : (
          {status.hasProfile && status.hasMember ? (
            <p>`Your referral link is sampleapp.com/?r=${profile?.account?.name}`</p>
          ) : (
            <p>`Use the button below to create your referral link</p>
            // Add button code and handler here
          )}
        )}
      )}
    </div>
  </div>
)

An example scenario about checking if state is loaded and ready to display:

  • if status.init is undefined or false, buddyLink and buddyState are still initializing. Display a loading state.
  • if status.init == true, buddyLink and buddyState have initialized successfully
    • Then, we can check how the user should be handled in the reward flow:
      • if status.hasMember or status.hasTreasuries or status.hasProfile is false, user is not setup, prompt them to generate link
      • if status.hasMember and status.hasTreasuries and status.hasProfile are true, user is setup, display their link using values pulled from profile and member

An example scenario of controlling render logic for treasury values:

  • if status.init == true , init has happened
    • then if loading.isLoadingTreasuries == true, we know that treasuries are fetching, we display loading state for treasury values
    • if loading.isLoadingTreasuries == false and status.hasTreasuries == false we know the user has no treasuries, we don't display balances, we prompt user to create treasuries
    • if loading.isLoadingTreasuries == false and status.hasTreasuries == true we know user has treasuries and we can check specific treasuries exist and their balances
      • the array of current user treasuries can be accessed with const [treasuries] = useBuddyState(BUDDY_TREASURIES); and we can filter those objects to check specific mintAddresses.
        • If they exist, display balance and allow user to claim
        • if they don't exist, prompt user to create a treasury for that mintAddress

Additional Components

The Buddy.Link SDK comes packaged with several helpful components to accelerate and simplify frontend development.

Components include:

  • BuddyState, an opinionated state management solution that avoids complexities like desychronized changes in apps with several shared contexts.
  • transactionAssembler, a transaction builder that combines your core instructions, compute and priority inputs, and optional memos. Future versions will include more options such as nonce support and versioned transactions.
  • getTokenBalance and getTokenAccountBalance are helpers to get the balance of any SPL token held by a wallet including handling decimal math and missing token accounts. Future versions will include support for additional token standards.

Additional Resources