@algorandfoundation/algoland-sdk
v1.1.0
Published
A TypeScript SDK for interacting with the Algoland smart contract on Algorand.
Maintainers
Keywords
Readme
Algoland SDK
A TypeScript SDK for interacting with the Algoland smart contract on Algorand.
Installation
npm install @algorandfoundation/algoland-sdkRequirements
Your package should install the peer requirements with the exact version matcher:
{
"dependencies": {
"@algorandfoundation/algokit-utils": "^9.0.0",
"algosdk": "^3.0.0"
}
}Confirm the SDK's package.json for the latest peerDependencies.
Usage
Creating an SDK Instance
import { AlgolandSDK } from '@algorandfoundation/algoland-sdk';
import { AlgorandClient } from '@algorandfoundation/algokit-utils/types/algorand-client';
const appId = 3215540125n; // Mainnet contract App ID
const algorand = AlgorandClient.mainNet()
// Basic instantiation
const sdk = new AlgolandSDK({ appId, algorand });Usage with @txnlab/use-wallet
import { useWallet } from '@txnlab/use-wallet';
const { algodClient, transactionSigner, activeAddress } = useWallet();
const algorand = AlgorandClient.fromClients({ algod: algodClient });
const sdk = new AlgolandSDK({ appId, algorand });API Reference
Global State
Get the global state of the Algoland contract:
const globalState = await sdk.getGlobalState();
// Returns:
// {
// admin: "ADMIN_ADDRESS_HERE...",
// userCounter: 1234n,
// drawAppId: 5678n,
// backend: "BACKEND_ADDRESS_HERE..."
// }User Methods
Get User by ID or Address
// Get user by ID
const userById = await sdk.getUser({ userId: 1n });
// Returns:
// {
// address: "ABCDEF..FEDCBA",
// relativeId: 1,
// referrerId: 0,
// numReferrals: 3,
// referrals: [2, 5, 8],
// points: 59, // 30 (own quests) + 9 (referral points) + 20 (redeemed back in)
// redeemedPoints: 20, // Points that were redeemed from prizes
// completedQuests: [1, 2, 3], // 3 completed quests = 30 points
// completableChallenges: [2],
// completedChallenges: [1],
// weeklyDrawEligibility: [1],
// availableDrawPrizeAssetIds: [123456n, 789012n],
// claimedDrawPrizeAssetIds: [111111n],
// displayPoints: 5900, // 59 × 100
// displayRedeemedPoints: 2000, // 20 × 100
// referralPoints: 9, // 59 - 30 (quest points) - 20 (redeemed) = 9
// displayReferralPoints: 900 // 9 × 100
// }
// Get user by address
const userByAddress = await sdk.getUser({ userAddress: 'ABCDEF..FEDCBA' });
// Returns the same structure as above, or undefined if user not foundGet Multiple Users
const userAddresses = ['ADDRESS1', 'ADDRESS2', 'ADDRESS3'];
const usersMap = await sdk.getUsers({ userAddresses });
// Returns: Map<string, AlgolandUser | undefined>
// Map {
// 'ADDRESS1' => { address: 'ADDRESS1', relativeId: 1, ... },
// 'ADDRESS2' => undefined, // user not found
// 'ADDRESS3' => { address: 'ADDRESS3', relativeId: 5, ... }
// }Get All Users
const allUsersMap = await sdk.getAllUsers();
// Returns: Map<string, AlgolandUser | undefined>
// Same structure as getUsers but contains all users registered in the systemGet User Referrals
// By user ID
const referrals = await sdk.getUserReferrals({ userId: 1 });
// Returns: string[]
// ['ADDRESS1', 'ADDRESS2', 'ADDRESS3']
// By user address
const referrals = await sdk.getUserReferrals({ userAddress: 'ADDRESS' });
// Returns: string[] - array of referral addressesQuest Methods
Get All Quests
const questsMap = await sdk.getQuests();
// Returns: Map<number, AlgolandQuest>
// Map {
// 1 => { id: 1, name: "Complete First Challenge", challengeId: 1 },
// 2 => { id: 2, name: "Refer 5 Friends", challengeId: 2 },
// 3 => { id: 3, name: "Earn 100 Points", challengeId: 1 }
// }Get Quest by ID
const quest = await sdk.getQuestById(1);
// Returns: AlgolandQuest | undefined
// { id: 1, name: "Complete First Challenge", challengeId: 1 }
// or undefined if quest not foundChallenge Methods
Get All Challenges
const challengesMap = await sdk.getChallenges();
// Returns: Map<number, AlgolandChallenge>
// Map {
// 1 => {
// id: 1,
// questIds: [1, 2],
// completionBadgeAssetId: 123456n,
// timeStart: 1640995200,
// timeEnd: 1672531200,
// numRequiredQuestsCompleted: 2,
// drawPrizeAssetIds: [789012n],
// numDrawEligibleAccounts: 100,
// numDrawWinners: 10,
// winners: [1, 5, 12, 23, 45]
// }
// }Get Challenge by ID
const challenge = await sdk.getChallengeById(1);
// Returns: AlgolandChallenge | undefined
// {
// id: 1,
// questIds: [1, 2],
// completionBadgeAssetId: 123456n,
// timeStart: 1640995200,
// timeEnd: 1672531200,
// numRequiredQuestsCompleted: 2,
// drawPrizeAssetIds: [789012n],
// numDrawEligibleAccounts: 100,
// numDrawWinners: 10,
// winners: [1, 5, 12, 23, 45]
// }
// or undefined if challenge not foundTransaction Methods
User Enrollment
Enroll a user into the campaign:
const { transactionSigner, activeAddress } = useWallet();
const result = await sdk.enrollCampaign({
referrerId: 0n, // no referrer, or use a valid user ID
sender: activeAddress,
signer: transactionSigner,
});
// Returns: SendAppTransactionResult with transaction details and app return values
// {
// transaction: Transaction,
// confirmation: PendingTransactionResponse,
// return: {
// relativeId: 123, // user's new relative ID
// referrerId: 0, // referrer ID (0 if no referrer)
// numReferrals: 0, // initial referral count
// referrals: [], // initial empty referrals array
// points: 0, // initial points
// completedQuests: [], // initial empty completed quests
// completableChallenges: [], // challenges available to complete
// completedChallenges: [], // initial empty completed challenges
// availableDrawPrizeAssetIds: [] // initial empty available prizes
// }
// }Complete Challenge
Complete a challenge and claim the badge:
const result = await sdk.completeChallenge({
challengeId: 1,
sender: activeAddress,
signer: transactionSigner,
});
// Returns: SendAtomicTransactionComposerResults
// {
// transactions: Transaction[],
// confirmations: PendingTransactionResponse[],
// returns: [], // void method returns empty array
// txIds: ['TXN_ID_1', 'TXN_ID_2'] // may include asset opt-in transaction
// }Admin Methods
Note: These methods require admin or backend permissions
Quest Management
// Complete a quest for a user (backend only)
const result = await sdk.completeQuest({
questId: 1,
user: 'USER_ADDRESS',
sender: backendAddress,
signer: backendSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }
// Add a new quest (admin only)
const result = await sdk.addQuest({
questId: 1,
name: 'Quest Name',
challengeId: 1,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }
// Delete a quest (admin only)
const result = await sdk.deleteQuest({
questId: 1,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }Challenge Management
// Add a new challenge (admin only)
const result = await sdk.addChallenge({
challengeId: 1,
challenge: challengeObject,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }
// Edit an existing challenge (admin only)
const result = await sdk.editChallenge({
challengeId: 1,
challenge: updatedChallengeObject,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }
// Delete a challenge (admin only)
const result = await sdk.deleteChallenge({
challengeId: 1,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }Asset Management
// Opt the contract into an asset (admin only)
const result = await sdk.optinAsset({
asset: assetId,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }
// Opt in and send asset to contract (admin only)
const result = await sdk.optinAndSendAsset({
asset: assetId,
amount: 1000n,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAtomicTransactionComposerResults
// {
// transactions: Transaction[], // optin + transfer transactions
// confirmations: PendingTransactionResponse[],
// returns: [],
// txIds: ['OPTIN_TXN_ID', 'TRANSFER_TXN_ID']
// }Administrative Functions
// Change backend address (admin only)
const result = await sdk.changeBackend({
newBackend: 'NEW_BACKEND_ADDRESS',
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }
// Change admin address (admin only)
const result = await sdk.changeAdmin({
newAdmin: 'NEW_ADMIN_ADDRESS',
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }
// Change draw app ID (admin only)
const result = await sdk.changeDrawAppId({
newDrawAppId: 5678n,
sender: adminAddress,
signer: adminSigner,
});
// Returns: SendAppTransactionResult (void method)
// { transaction: Transaction, confirmation: PendingTransactionResponse }Types
The SDK exports all relevant types from the generated client and defines additional helper types:
import {
AlgolandUser,
AlgolandQuest,
AlgolandChallenge,
AlgolandGlobalState,
DrawGlobalState,
AlgolandWeeklyDrawState,
NFTSpecWithCID,
PointsNFTSpecWithCID
} from '@algorandfoundation/algoland-sdk';Core Types
AlgolandUser
Represents a user in the Algoland system with all their quest progress and achievements:
type AlgolandUser = {
address: string; // User's Algorand address
relativeId: number; // Small incremental user ID (1, 2, 3...)
referrerId: number; // RelativeId of the referrer
numReferrals: number; // Number of referrals
referrals: number[]; // Referrals' relative IDs
points: number; // Accumulated points
redeemedPoints: number; // Redeemed points: points from redeeming Points prize assets
completedQuests: number[]; // Individual quests completed
completableChallenges: number[]; // Challenges that are ready to complete (by claiming NFT)
completedChallenges: number[]; // Completed challenges
weeklyDrawEligibility: number[]; // Weekly draw eligibility: completed challenges in time (values are challengeId)
availableDrawPrizeAssetIds: bigint[]; // Available prizes (asset IDs) from weekly/final VRF draws
claimedDrawPrizeAssetIds: bigint[]; // Claimed prizes (asset IDs) from weekly/final VRF draws
// Computed properties (getters)
displayPoints: number; // Display-friendly points value (multiplied by display multiplier)
displayRedeemedPoints: number; // Display-friendly redeemed points value (multiplied by display multiplier)
referralPoints: number; // Points earned from referrals (total points minus own quest points and redeemed points)
displayReferralPoints: number; // Display-friendly referral points value (multiplied by display multiplier)
}AlgolandQuest
Represents a quest that users can complete to earn points and progress in challenges:
type AlgolandQuest = {
id: number; // Unique quest identifier
name: string; // Human-readable quest name
challengeId: number; // ID of the challenge this quest belongs to
}AlgolandChallenge
Represents a challenge that groups multiple quests and offers rewards:
type AlgolandChallenge = {
id: number; // Unique challenge identifier
questIds: number[]; // Quest IDs for this challenge
completionBadgeAssetId: bigint; // Badge asset ID given out for completing the challenge (including after time end)
timeStart: number; // Start time of challenge
timeEnd: number; // End time of challenge. Only affects eligibility for weekly VRF draw. Challenge can be completed after this time
numRequiredQuestsCompleted: number; // Number of quest completions required to mark this challenge as complete. Usually equals to number of quests
drawPrizeAssetIds: bigint[]; // Asset prize asset IDs for weekly draws
numDrawEligibleAccounts: number; // Tracks the number of accounts eligible for the weekly VRF draw
numDrawWinners: number; // Number of winners to be picked at the weekly VRF draw
winners: number[]; // Relative ID of weekly draw winners
}AlgolandGlobalState
Represents the global state of the Algoland smart contract:
type AlgolandGlobalState = {
admin: string; // Address of the contract administrator
userCounter: bigint; // Total number of registered users
drawAppId: bigint; // App ID of the weekly draw contract
backend: string; // Address authorized to complete quests
}DrawGlobalState
Represents the global state of the weekly draw smart contract:
type DrawGlobalState = {
admin: string; // Address of the draw contract administrator
backend: string; // Address authorized to operate the draw contract
beaconAppId: bigint; // App ID of the VRF beacon contract
registryAppId: bigint; // App ID of the main Algoland registry contract
}AlgolandWeeklyDrawState
Represents the state of a weekly draw for a specific challenge:
type AlgolandWeeklyDrawState = {
status: 'BLD' | 'RST' | 'RUN' | 'END'; // Build/Reset/Run/End status of the draw
accountsIngested: bigint; // Number of accounts processed for the draw
lastRelativeId: bigint; // Last user relative ID processed
commitBlocks: bigint[]; // Block numbers used for VRF commit rounds
winners: number[]; // Relative IDs of draw winners
txIds: Uint8Array[]; // Transaction IDs associated with the draw
}NFTSpecWithCID
Represents an NFT specification with optional IPFS CID:
type NFTSpecWithCID = {
total: bigint; // Total supply of the NFT
assetName: string; // Asset name
unitName: string; // Unit name
url: string; // Asset URL
freeze: string; // Freeze address
clawback: string; // Clawback address
reserve: string; // Reserve address
metadataHash: Uint8Array; // Metadata hash (32 bytes)
ipfsCid?: string; // Optional IPFS CID for metadata
}PointsNFTSpecWithCID
Represents an NFT specification for points-based assets:
type PointsNFTSpecWithCID = NFTSpecWithCID & {
points: number; // Points value associated with this NFT
}Event Types
These types represent events emitted by the smart contract (useful for indexing and monitoring):
QuestCompletionEvent
Emitted when a user completes a quest:
type QuestCompletionEvent = {
account: string; // Address of the user completing the quest
userId: number; // Relative ID of the user
questId: number; // ID of the completed quest
challengeId: number; // ID of the challenge the quest belongs to
}ChallengeCompletableEvent
Emitted when a user becomes eligible to complete a challenge:
type ChallengeCompletableEvent = {
account: string; // Address of the user
userId: number; // Relative ID of the user
challengeId: number; // ID of the challenge now available to complete
}ChallengeCompletedEvent
Emitted when a user completes a challenge and claims the NFT:
type ChallengeCompletedEvent = {
account: string; // Address of the user
userId: number; // Relative ID of the user
challengeId: number; // ID of the completed challenge
}PointsEvent
Emitted when points are awarded to a user:
type PointsEvent = {
account: string; // Address of the user receiving points
userId: number; // Relative ID of the user receiving points
questId: number; // ID of the quest that generated the points
addedPoints: number; // Number of points added in this event
totalPoints: number; // User's total points after this addition
actorUserId: number; // ID of the user who triggered the points:
// - If points from own completion: actorUserId === userId
// - If referral points: actorUserId = referred user, userId = referrer
}Type Usage Examples
Working with Users
const user: AlgolandUser = await sdk.getUser({ userId: 1n });
if (user) {
console.log(`User ${user.address} has ${user.points} points (display: ${user.displayPoints})`);
// Output: User ABCDEF..FEDCBA has 59 points (display: 5900)
console.log(`Completed quests: ${user.completedQuests.join(', ')}`);
// Output: Completed quests: 1, 2, 3
console.log(`Ready to claim challenges: ${user.completableChallenges.join(', ')}`);
// Output: Ready to claim challenges: 2
console.log(`Completed challenges: ${user.completedChallenges.join(', ')}`);
// Output: Completed challenges: 1
// Check points breakdown
console.log(`Total display points: ${user.displayPoints}`);
// Output: Total display points: 5900
console.log(`Referral display points: ${user.displayReferralPoints}`);
// Output: Referral display points: 900
console.log(`Redeemed display points: ${user.displayRedeemedPoints}`);
// Output: Redeemed display points: 2000
// Check weekly draw eligibility
if (user.weeklyDrawEligibility.length > 0) {
console.log(`Eligible for weekly draws: ${user.weeklyDrawEligibility.join(', ')}`);
// Output: Eligible for weekly draws: 1
}
// Check available prizes
if (user.availableDrawPrizeAssetIds.length > 0) {
console.log(`Available prizes: ${user.availableDrawPrizeAssetIds.join(', ')}`);
// Output: Available prizes: 123456, 789012
}
// Check if user has referrals
if (user.numReferrals > 0) {
console.log(`User has referred ${user.numReferrals} users`);
// Output: User has referred 3 users
const referralAddresses = await sdk.getUserReferrals({ userId: user.relativeId });
console.log(`Referral addresses: ${referralAddresses.join(', ')}`);
// Output: Referral addresses: ADDRESS1, ADDRESS2, ADDRESS3
}
}Working with Challenges
const challenge: AlgolandChallenge = await sdk.getChallengeById(1);
if (challenge) {
const now = Math.floor(Date.now() / 1000);
const isActive = now >= challenge.timeStart && now <= challenge.timeEnd;
console.log(`Challenge "${challenge.id}" is ${isActive ? 'active' : 'inactive'}`);
console.log(`Requires ${challenge.numRequiredQuestsCompleted} quests to complete`);
console.log(`Completion badge: Asset ID ${challenge.completionBadgeAssetId}`);
if (challenge.winners.length > 0) {
console.log(`Draw winners: ${challenge.winners.join(', ')}`);
}
}Error Handling
All async methods may throw errors. It's recommended to wrap calls in try-catch blocks:
try {
const user = await sdk.getUser({ userId: 1n });
console.log('User found:', user);
} catch (error) {
console.error('Failed to get user:', error.message);
}