A secure NFT staking program on Solana (Anchor) that locks NFTs in escrow PDAs for a fixed period. Other on-chain or off-chain systems can read stake state to gate access to benefits such as fee claims.
This program can be integrated with Balddev DEX.
7dMir6E96FwiYQQ9mdsL6AKUmgzzrERwqj7mkhthxQgV
The program uses a binary lock model:
- NFT is either actively staked (locked) or unstaked (unlocked)
- No reward multipliers or complex reward math inside this program
- External systems decide how stake status affects eligibility or incentives
- NFT escrow via per-user PDA authority
- Collection-level allowlisting and enable/disable controls
- Configurable min/max lock durations per collection
- Emergency pause for staking operations
- No admin withdrawal path for user NFTs
- Event emission for indexing and analytics
- Support for traditional and compressed NFT tracking fields
Initializes global config. Callable once.
Parameters:
protocol_fee_bps(u16) - protocol fee in bps (max 1000 = 10%)
Access:
- First successful caller initializes config authority
Updates global config.
Parameters:
new_authority(Option<Pubkey>) - transfer authoritypaused(Option<bool>) - pause/unpause staking opsprotocol_fee_bps(Option<u16>) - update fee bps
Access:
- Current authority only
Adds or updates config for a verified NFT collection.
Parameters:
collection(Pubkey) - verified collection addressmin_lock_duration(i64) - minimum lock (seconds)max_lock_duration(i64) - maximum lock (seconds)enabled(bool) - staking enabled for this collection
Access:
- Authority only
Locks a user NFT in escrow and creates stake state.
Parameters:
lock_duration(i64) - lock period in secondsassociated_pool(Option<Pubkey>) - optional pool/market reference for downstream integrations
Requirements:
- Signer owns the NFT
- Collection is allowlisted and enabled
- Lock duration is within collection bounds
- NFT metadata verification passes
Unlocks and returns NFT after lock expiry.
Requirements:
- Signer matches stake owner
- Current timestamp is at or after
unlock_at
{
authority: Pubkey,
paused: bool,
bump: u8,
_reserved: [u8; 6],
total_stakes: u64,
collection_count: u32,
protocol_fee_bps: u16,
_padding: [u8; 128]
}PDA seeds:
["config"]
{
collection: Pubkey,
enabled: bool,
bump: u8,
min_lock_duration: i64,
max_lock_duration: i64,
_reserved: [u8; 8],
total_staked: u64,
lifetime_stakes: u64,
_padding: [u8; 128]
}PDA seeds:
["collection", collection_pubkey]
{
owner: Pubkey,
nft_mint: Pubkey,
collection: Pubkey,
staked_at: i64,
unlock_at: i64,
lock_duration: i64,
bump: u8,
is_active: bool,
_reserved: [u8; 6],
associated_pool: Pubkey,
nft_type: u8,
leaf_index: u64,
_padding: [u8; 135]
}PDA seeds:
["stake", nft_mint, owner]
Per-user authority PDA that owns the escrow token account.
PDA seeds:
["escrow_authority", owner]
This staking program can be integrated into Balddev DEX by checking whether a user has an active, still-locked stake.
Example check logic:
let stake_account = StakeAccount::try_deserialize(&stake_account_data)?;
let is_staked = stake_account.is_active && !stake_account.is_unlocked(current_time);
if is_staked {
// eligible in Balddev DEX
} else {
// staking requirement not met
}Integration patterns:
- On-chain check
- Balddev DEX instruction reads
StakeAccountdirectly - Highest consistency, higher compute/account overhead
- Off-chain indexer check
- Index
NftStakedandNftUnstakedevents - Maintain stake status in a database/cache
- Lower on-chain cost, requires reliable indexer infra
{
staker: Pubkey,
nft_mint: Pubkey,
collection: Pubkey,
staked_at: i64,
unlock_at: i64,
lock_duration: i64,
stake_account: Pubkey
}{
staker: Pubkey,
nft_mint: Pubkey,
unstaked_at: i64,
total_staked_duration: i64
}{
collection: Pubkey,
min_lock_duration: i64,
max_lock_duration: i64,
enabled: bool
}{
authority: Pubkey,
paused: bool,
updated_at: i64
}CollectionNotWhitelisted
StillLocked
InvalidLockDuration
InvalidNftOwner
InvalidMetadata
StakeAccountMismatch
InvalidAuthority
CollectionConfigNotFound
ArithmeticOverflow
InvalidMultiplier
StakeAlreadyExists
ProgramPaused
InvalidTokenAccount
MetadataVerificationFailed
InvalidNftType
InvalidMerkleProof
CompressedNftVerificationFailed
InvalidDelegate
InvalidMerkleTreeanchor build
anchor testDeploy:
# devnet
anchor deploy --provider.cluster devnet
# mainnet
anchor deploy --provider.cluster mainnetSee SECURITY.txt for threat model, assumptions, and operational guidance.
See LICENSE.