Skip to content

Using the SDK

Use the SDK to integrate Stack into your app in minutes. The SDK is available in Typescript and Javascript.

Get Started

Get your API key

Sign up at stack.so/signup to get your API key and create a point system. You can continue to track your point system in the Stack dashboard.

Install

Add the SDK to your project.

bash
npm install @stackso/js-core

Initialize the client

Use the StackClient to initialize the client, providing your API key and point system id from the Stack dashboard.

typescript
import {StackClient} from "@stackso/js-core";

// Initialize the client
const stack = new StackClient({
  // Get your API key and point system id from the Stack dashboard (stack.so)
  apiKey: "YOUR_API_KEY", 
  pointSystemId: "YOUR_POINT_SYSTEM_ID",
});

Add points and tag the event

Tagging each point assignment with an event name is important for tracking and analytics.

typescript
await stack.track("user_signup", {
  points: 10,
  account: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"
});

await stack.track("user_signup", {
  points: 15,
  account: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5",
});

Get points for an account

Get the points for an account using the getPoints method, providing the account address.

typescript
await stack.getPoints("0x627306090abaB3A6e1400e9345bC60c78a8BEf57");
// => 10

Parameters

  • account: The Ethereum address for which you want to get the points.

Returns

  • number: The total points for the account.

Get points for a list of accounts

You can also use getPoints with an array. Simply pass an array of account addresses to get the points for multiple accounts.

typescript
await stack.getPoints([
  "0x627306090abaB3A6e1400e9345bC60c78a8BEf57",
  "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5"
]);

// => [
//   {account: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57", points: 10},
//   {account: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5", points: 15}
// ]

Get points by eventType

You can also filter by eventType when fetching points.

typescript
await stack.getPoints([
  "0x627306090abaB3A6e1400e9345bC60c78a8BEf57",
  "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5"
], { event: "user_signup" });

// => [
//   {account: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57", points: 10},
//   {account: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5", points: 15}
// ]

Get a leaderboard

Get the leaderboard using the getLeaderboard method. The response defaults to the top 100 users, and you can specify the limit and offset to paginate through the leaderboard.

typescript
await stack.getLeaderboard();
// => [
//   {account: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5", points: 15}, 
//   {account: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57", points: 10}
// ]

// Pagination 
await stack.getLeaderboard({limit: 10, offset: 20});

Get events

Get the events for a specific event name using the getEvents method.

typescript
await stack.getEvents({
  address: "0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E", // optional filter by address
  event: 'test_event', // optional filter by event name
  limit: 1, // optional, for pagination
  offset: 1 // optional, for pagination
});

// Returns an array of events
// => [
//   {
//     event: 'test_event',
//     address: '0xCC65fA278B917042822538c44ba10AD646824026',
//     timestamp: '2024-03-23T16:14:24.898Z',
//     points: 200,
//     metadata: {}
//   }
// ]

Get Leaderboard Rank

Get the rank of an account in the leaderboard using the getLeaderboardRank method.

typescript
await stack.getLeaderboardRank("0x2eeb301387D6BDa23E02fa0c7463507c68b597B5");

// {
//   address: '0x627306090abaB3A6e1400e9345bC60c78a8BEf57',
//   rank: 1,
//   points: 10
// }

Handling duplicate events

The SDK provides a parameter uniqueId that uniquely identifies an event type for your point system and will only track the first event we receive with this uniqueId.

typescript
// sending this "user_first_referral" event twice will result in only
// the first one being processed
await stack.track("user_first_referral", {
  points: 15,
  account: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5",
  uniqueId: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5",
});

Some ideas for uniqueId include:

A unique identifier in your system

For example, a userId that is associated with multiple EOAs that you want to prevent from being double counted.

typescript
// Sending this "user_first_referral" event twice will result in only
// the first one being processed.
await stack.track("user_first_referral", {
  points: 15,
  account: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5",
  uniqueId: "2540-4a99-83c2-3d7f", // a unique userId in your system
});

A unique timeframe

For a wallet you only want to log at most 1 event per day, using a date string in the uniqueId (e.g. 2024-04-01) – will prevent the same event from being logged more than once per day.

typescript
await stack.track("DailyWalletLogin", {
  points: 15,
  account: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5",
  uniqueId: `${(new Date()).toISOString().split('T')[0]}-0x2eeb301387D6BDa23E02fa0c7463507c68b597B5`,
});

Creating Redemptions

A Redemption offer represents a reward that a user can redeem points to earn.

Creating a redemption

typescript
const { id: redemptionOfferId } = await stack.createRedemptionOffer({
  name: "Stack Baseball Hat Redemption",
  points: 200, // number of points required for redemption
  type: RedemptionOfferType.OFFCHAIN,
});

Parameters

  • name: The name of the redemption offer
  • type: OFFCHAIN means that there is no onchain transaction during redemption
  • points: The number of points required for one redemption

Returns

  • id: The unique ID for the redemption offer
  • name: The specified name of the redemption offer
  • type: The type of redemption
  • status: "ACTIVE" if the offer is active
  • points: The number of points required for the redemption
  • createdAt: Timestamp for creation
  • updatedAt: Timestamp for last change to the redemption offer

Fetch redemption offers

Get a list of redemptions for the current point system.

typescript
await stack.getRedemptionOffers();

// [{
//     id: 30,
//     name: 'Stack Baseball Hat Redemption',
//     type: 'OFFCHAIN',
//     status: 'ACTIVE',
//     points: 200,
//     createdAt: '2024-04-17T18:32:41.050Z',
//     updatedAt: '2024-04-17T18:32:41.050Z'
// }]

Redeem

Redeeming for a reward will subtract points from the user's point balance and track an event.

typescript
await stack.redeem(redemptionOfferId, {
  address: "0x2eeb301387D6BDa23E02fa0c7463507c68b597B5"
});
// Tracks an redeem event and deducts 200 points from 0x2eeb301387D6BDa23E02fa0c7463507c68b597B5

// {
//   address: '0x2eeb301387D6BDa23E02fa0c7463507c68b597B5',
//   redemptionOfferId: 30,
//   claimedAt: "2024-04-17T18:32:44.222Z"
// }

Other users can also redeem. However, each user can only redeem once.

typescript
await stack.redeem(redemptionOfferId, {
  address: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"
});

// {
//   address: '0x627306090abaB3A6e1400e9345bC60c78a8BEf57',
//   redemptionOfferId: 30,
//   claimedAt: "2024-04-17T18:34:44.222Z"
// }

// Will throw an error if called again
await stack.redeem(redemptionOfferId, {
  address: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"
});

Fetch redemption claims

Fetch all of the users that have redeeemed for a particular redemption offer.

typescript
await stack.getRedemptionClaims({
  redemptionOfferId: 30,
});

// [{
//   address: '0x2eeb301387D6BDa23E02fa0c7463507c68b597B5',
//   redemptionOfferId: 30,
//   claimedAt: '2024-04-17T18:32:44.222Z',
// }, {
//   address: '0x627306090abaB3A6e1400e9345bC60c78a8BEf57',
//   redemptionOfferId: 30,
//   claimedAt: '2024-04-17T18:34:44.222Z',
// }]

Best Practices

  • Make sure to keep your API key secure and do not expose it in client-side code.
  • Use meaningful event names that clearly describe the action being performed.
  • Consider using the uniqueId parameter to prevent duplicate events from being tracked, especially for critical actions.
  • Regularly monitor your point system in the Stack dashboard to ensure accurate tracking and make any necessary adjustments.