@tomb-labs/staking
v0.7.16
Published
TypeScript SDK for Tomb Labs Staking Program
Maintainers
Readme
Tomb Labs Staking SDK
A TypeScript SDK for interacting with the Tomb Labs Staking Program on Solana. This SDK provides a comprehensive interface for managing NFT staking operations, including support for both standard and programmable NFTs.
Features
- Comprehensive TypeScript Support: Fully typed API with detailed type definitions
- Complete Program Interaction: Methods for all program instructions
- NFT Compatibility: Support for both standard and programmable NFTs (pNFTs)
- Enhanced Security: Built-in validation and error handling
- PDA Management: Utility functions for program-derived address derivation
- Account Definitions: Comprehensive account type definitions
- Data Fetching: Efficient account data fetching utilities
- React Integration: Ready for use with React applications
- Transaction Building: Flexible transaction building for custom signing workflows
- Wallet Compatibility: Works with any wallet adapter implementing the basic interface
Installation
npm install @tomb-labs/staking-sdkor
yarn add @tomb-labs/staking-sdkQuick Start
Using with a Custom Wallet
import { StakingClient } from '@tomb-labs/staking-sdk';
import { Connection, PublicKey, Transaction } from '@solana/web3.js';
// Your wallet must implement this interface
interface WalletAdapter {
publicKey: PublicKey | null;
signTransaction: <T extends Transaction>(tx: T) => Promise<T>;
signAllTransactions: <T extends Transaction>(txs: T[]) => Promise<T[]>;
}
// Initialize connection and wallet
const connection = new Connection('https://api.mainnet-beta.solana.com');
const wallet: WalletAdapter = {
// Your wallet implementation
};
// Create SDK client
const client = new StakingClient(connection, wallet);Using with Solana Wallet Adapter
import { StakingClient } from '@tomb-labs/staking-sdk';
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
// In your React component
const { connection } = useConnection();
const wallet = useWallet();
// Create SDK client - wallet-adapter-react implements the required interface
const client = new StakingClient(connection, wallet);Using Transaction Builder Mode
import { StakingClient } from '@tomb-labs/staking-sdk';
import { Connection, Keypair } from '@solana/web3.js';
// Initialize connection and wallet
const connection = new Connection('https://api.mainnet-beta.solana.com');
const keypair = Keypair.generate();
// Create a simple wallet adapter
const wallet = {
publicKey: keypair.publicKey,
signTransaction: async tx => {
tx.partialSign(keypair);
return tx;
},
signAllTransactions: async txs => {
return txs.map(tx => {
tx.partialSign(keypair);
return tx;
});
},
};
// Create SDK client
const client = new StakingClient(connection, wallet);
// Build transaction without signing
const tx = await client.projects.createProject({
name: 'my-project',
authority: wallet.publicKey,
image_url: 'https://example.com/image.png',
fees: {
stakeFee: 1000000,
unstakeFee: 1000000,
claimFee: 1000000,
},
buildOnly: true, // This flag makes the method return a Transaction instead of sending it
});
// Now you can sign and send the transaction however you want
const signature = await wallet.signTransaction(tx);
const txid = await connection.sendRawTransaction(signature.serialize());Usage Examples
Creating a Project
Projects are the top-level organizational unit in the staking program that can contain multiple pools.
try {
// Direct signing mode (default)
const signature = await client.projects.createProject({
name: 'my-project',
authority: wallet.publicKey,
image_url: 'https://example.com/image.png',
fees: {
stakeFee: 1000000, // Fee in lamports (1 SOL = 1,000,000,000 lamports)
unstakeFee: 1000000,
claimFee: 1000000,
},
});
console.log('Project created:', signature);
} catch (error) {
if (error instanceof StakingError) {
console.error('Staking error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}Creating a Staking Pool
Staking pools are where users can stake their NFTs to earn rewards.
try {
const signature = await client.pools.createPool({
name: 'my-nft-pool',
maxItemsStaked: 1000,
collectionKey: collectionPublicKey, // Optional: NFT collection address
requiresAuthorization: false,
rewardMint: rewardTokenMintPublicKey,
authority: wallet.publicKey,
accrualRate: 10, // Tokens per day per NFT
accrualPeriod: 'daily', // Options: 'hourly', 'daily', 'weekly'
});
console.log('Pool created:', signature);
} catch (error) {
if (error instanceof StakingError) {
console.error('Staking error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}Staking an NFT
The SDK supports staking both standard NFTs and programmable NFTs.
try {
// For standard NFTs
const signature = await client.staker.stakeStandard({
name: 'my-nft-pool',
authority: wallet.publicKey,
projectAuthority: projectAuthorityPublicKey,
rewardMint: rewardMintPublicKey,
nftMint: nftMintPublicKey,
});
console.log('NFT staked:', signature);
} catch (error) {
if (error instanceof StakingError) {
// Handle specific staking errors
switch (error.message) {
case ERRORS.POOL_INACTIVE:
console.error('Pool is not active');
break;
case ERRORS.INVALID_COLLECTION:
console.error('NFT is not from the required collection');
break;
default:
console.error('Staking error:', error.message);
}
} else {
console.error('Unexpected error:', error);
}
}Staking a Programmable NFT (pNFT)
try {
// For programmable NFTs
const signature = await client.staker.stakeProgrammable({
name: 'my-nft-pool',
authority: wallet.publicKey,
projectAuthority: projectAuthorityPublicKey,
rewardMint: rewardMintPublicKey,
nftMint: nftMintPublicKey,
// Additional parameters for programmable NFTs
tokenStandard: 4, // TokenStandard.ProgrammableNonFungible
ruleset: rulesetPublicKey, // Optional: ruleset for the pNFT
});
console.log('Programmable NFT staked:', signature);
} catch (error) {
if (error instanceof StakingError) {
console.error('Staking error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}Claiming Rewards
try {
const signature = await client.staker.claimReward({
name: 'my-nft-pool',
authority: wallet.publicKey,
projectAuthority: projectAuthorityPublicKey,
rewardMint: rewardMintPublicKey,
stakedItem: stakedItemPublicKey, // The public key of the staked item account
});
console.log('Rewards claimed:', signature);
} catch (error) {
if (error instanceof StakingError) {
if (error.message === ERRORS.NO_REWARDS) {
console.log('No rewards available to claim');
} else {
console.error('Claim error:', error.message);
}
}
}Claiming All Rewards
To claim rewards for all staked items in a pool:
try {
// First, fetch all staked items for this user
const stakedItems = await client.fetcher.getAllStakedItemsForStaker({
authority: wallet.publicKey,
pool: poolPublicKey,
});
// Use the enhanced instructions to claim all rewards
const transactions = await client.enhanced.claimAll(
{
name: 'my-nft-pool',
authority: wallet.publicKey,
projectAuthority: projectAuthorityPublicKey,
rewardMint: rewardMintPublicKey,
},
stakedItems
);
// Send each transaction
for (const tx of transactions) {
const signature = await wallet.sendTransaction(tx, connection);
console.log('Claimed rewards:', signature);
}
} catch (error) {
console.error('Error claiming rewards:', error);
}Unstaking an NFT
try {
const signature = await client.staker.unstake({
name: 'my-nft-pool',
authority: wallet.publicKey,
projectAuthority: projectAuthorityPublicKey,
rewardMint: rewardMintPublicKey,
nftMint: nftMintPublicKey,
});
console.log('NFT unstaked:', signature);
} catch (error) {
if (error instanceof StakingError) {
if (error.message === ERRORS.LOCKUP_ACTIVE) {
console.error('NFT is still in lockup period and cannot be unstaked yet');
} else {
console.error('Unstake error:', error.message);
}
}
}Account Data Fetching
The SDK provides a dedicated Fetcher class for efficient account data retrieval:
// Access the fetcher
const fetcher = client.fetcher;
// Get global state and fees
const globalState = await fetcher.getGlobalState();
const fees = await fetcher.getGlobalFees();
// Get project information
const project = await fetcher.getProjectState({
authority: authorityPublicKey,
});
const allProjects = await fetcher.getAllProjects();
// Get pool information
const pool = await fetcher.getPoolState({
authority: authorityPublicKey,
name: 'my-nft-pool',
rewardMint: rewardMintPublicKey,
});
const projectPools = await fetcher.getAllPoolsForProject({
authority: authorityPublicKey,
});
// Get staker information
const staker = await fetcher.getStakerState({
authority: wallet.publicKey,
pool: poolPublicKey,
});
const allStakers = await fetcher.getAllStakersForPool({
pda: poolPublicKey,
});
// Get staked item information
const stakedItems = await fetcher.getAllStakedItemsForStaker({
authority: wallet.publicKey,
pool: poolPublicKey,
});Error Handling
The SDK provides a standardized error system to handle common failures:
import { StakingError, ERRORS } from '@tomb-labs/staking-sdk';
try {
// SDK operation
} catch (error) {
if (error instanceof StakingError) {
// Handle known staking errors
switch (error.message) {
case ERRORS.POOL_INACTIVE:
console.error('Pool is not active');
break;
case ERRORS.POOL_FULL:
console.error('Pool has reached maximum capacity');
break;
case ERRORS.INVALID_COLLECTION:
console.error('NFT is not from the required collection');
break;
case ERRORS.NO_REWARDS:
console.error('No rewards available to claim');
break;
case ERRORS.LOCKUP_ACTIVE:
console.error('NFT is still in lockup period');
break;
case ERRORS.COOLDOWN_ACTIVE:
console.error('Action is on cooldown');
break;
default:
console.error('Staking error:', error.message);
}
} else {
// Handle unexpected errors
console.error('Unexpected error:', error);
}
}Advanced Usage
Using Enhanced Instructions
The SDK provides enhanced instructions that handle common operations with improved convenience:
// Get the enhanced instructions
const enhanced = client.enhanced;
// Stake an NFT with automatic staker initialization
const tx = await enhanced.stakeStandard({
name: 'my-nft-pool',
authority: wallet.publicKey,
projectAuthority: projectAuthorityPublicKey,
rewardMint: rewardMintPublicKey,
nftMint: nftMintPublicKey,
});
// Result is a transaction that will automatically initialize the staker account if needed
const signature = await wallet.sendTransaction(tx, connection);Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For support or questions, please open an issue on the GitHub repository.
