Skip to main content
Staking and Delegation hero

Overview

Starkzap supports Starknet’s native staking protocol. Validators run pools where delegators can stake tokens and earn rewards. What users can do with staking:
  • 💰 Earn passive income - Stake tokens (like STRK) and earn rewards over time, similar to a savings account with interest
  • 🏦 Support network security - Stake tokens to help secure the Starknet network while earning rewards
  • 📈 Grow their holdings - Rewards compound over time, allowing users to increase their token holdings without additional purchases
  • Flexible participation - Add more tokens anytime, claim rewards when ready, and exit when needed (with a cooldown period)
Real-world analogy: Think of staking like putting money in a high-yield savings account or certificate of deposit (CD). You lock up your tokens for a period, and in return, you earn interest (rewards). The longer you stake, the more you can potentially earn.
Staking contract is selected automatically from chain-aware presets. You only need staking.contract if you want to override the default.

Configuration

Staking works out of the box when using a supported chain preset:
import { StarkZap } from "starkzap";

const sdk = new StarkZap({
  network: "mainnet",
});
Optional override:
import { StarkZap, fromAddress } from "starkzap";

const sdk = new StarkZap({
  network: "mainnet",
  staking: {
    contract: fromAddress(
      "0x00ca1702e64c81d9a07b86bd2c540188d92a2c73cf5cc0e508d949015e7e84a7"
    ),
  },
});

Discovering Validators and Pools

Using Validator Presets

import { mainnetValidators, sepoliaValidators } from "starkzap";

// Use built-in validator presets
for (const validator of Object.values(mainnetValidators)) {
  console.log(`${validator.name}: ${validator.stakerAddress}`);
}
// Also available: sepoliaValidators

Getting Stakeable Tokens

const tokens = await sdk.stakingTokens();
console.log(tokens.map((t) => t.symbol)); // ["STRK", ...]

Getting Pools for a Validator

const pools = await sdk.getStakerPools(validator.stakerAddress);
for (const pool of pools) {
  console.log(`${pool.token.symbol}: ${pool.amount.toFormatted()} delegated`);
  console.log(`Pool contract: ${pool.poolContract}`);
}

Entering a Pool

Stake tokens for the first time in a validator’s pool. The SDK handles the token approval automatically.
const tx = await wallet.enterPool(poolAddress, Amount.parse("100", STRK));
await tx.wait();
This will:
  1. Approve the staking contract to spend your tokens (if needed)
  2. Call enter_delegation_pool on the staking contract
  3. Return a transaction you can track
Use wallet.stake() when you don’t want to branch on membership manually:
const tx = await wallet.stake(poolAddress, Amount.parse("100", STRK));
await tx.wait();
wallet.stake() automatically:
  • calls enter_delegation_pool for first-time stakers
  • calls add_to_delegation_pool for existing members

Adding to an Existing Stake

If you’re already a pool member, add more tokens:
const tx = await wallet.addToPool(poolAddress, Amount.parse("50", STRK));
await tx.wait();

Claiming Rewards

Users earn rewards continuously while their tokens are staked. Rewards accumulate over time and can be claimed at any time:
const position = await wallet.getPoolPosition(poolAddress);

if (position && !position.rewards.isZero()) {
  const tx = await wallet.claimPoolRewards(poolAddress);
  await tx.wait();
  console.log(`Claimed ${position.rewards.toFormatted()}`);
}
What users get: Rewards are paid in the same token they staked (e.g., if you stake STRK, you earn STRK rewards). Users can claim rewards and either:
  • Withdraw them to their wallet
  • Re-stake them to compound earnings (add them back to the pool)

Exiting a Pool (Two-Step Process)

Exiting a pool is a two-step process with an exit window:

Step 1: Declare Exit Intent

Tokens stop earning rewards immediately:
const tx = await wallet.exitPoolIntent(
  poolAddress,
  Amount.parse("50", STRK)
);
await tx.wait();

Step 2: Complete Withdrawal

Wait for the exit window, then complete the withdrawal:
const position = await wallet.getPoolPosition(poolAddress);
if (position?.unpoolTime && new Date() >= position.unpoolTime) {
  const tx = await wallet.exitPool(poolAddress);
  await tx.wait();
  console.log("Tokens returned to wallet");
}

Querying Position

Get detailed information about your staking position to show users their earnings:
const position = await wallet.getPoolPosition(poolAddress);

if (position) {
  console.log(`Staked:     ${position.staked.toFormatted()}`); // Original amount staked
  console.log(`Rewards:    ${position.rewards.toFormatted()}`); // Earned rewards (can claim)
  console.log(`Total:      ${position.total.toFormatted()}`); // Staked + rewards combined
  console.log(`Commission: ${position.commissionPercent}%`); // Validator's fee
  console.log(`Unpooling:  ${position.unpooling.toFormatted()}`); // Amount being withdrawn
  console.log(`Unpool at:  ${position.unpoolTime}`); // When withdrawal completes
}
What this shows users:
  • How much they’ve staked (their original investment)
  • How much they’ve earned in rewards (their passive income)
  • Their total position value (staked + rewards)
  • When they can withdraw if they’ve initiated an exit

Position Interface

interface PoolMember {
  staked: Amount;
  rewards: Amount;
  total: Amount;
  unpooling: Amount;
  unpoolTime?: Date;
  commissionPercent: number;
  rewardAddress: Address;
}

Checking Membership

const isMember = await wallet.isPoolMember(poolAddress);

Getting Commission Rate

const commission = await wallet.getPoolCommission(poolAddress);
console.log(`Commission: ${commission}%`);

Using Transaction Builder

The transaction builder makes it easy to combine staking operations with other actions:
const tx = await wallet
  .tx()
  // Transfer some tokens
  .transfer(STRK, [{ to: recipient, amount: Amount.parse("10", STRK) }])
  // Stake in a pool (auto-detects enter vs. add)
  .stake(poolAddress, Amount.parse("100", STRK))
  // Claim rewards from another pool
  .claimPoolRewards(anotherPoolAddress)
  .send();

await tx.wait();
The .stake() method automatically:
  • Calls enter_delegation_pool for new members
  • Calls add_to_delegation_pool for existing members

Staking Flow Examples

Complete Staking Flow

Here’s a complete example showing how users can stake, earn, and manage their position:
// 1. Discover pools
const pools = await sdk.getStakerPools(validator.stakerAddress);
const strkPool = pools.find((p) => p.token.symbol === "STRK");

// 2. Enter pool - User stakes 100 STRK and starts earning immediately
await wallet.enterPool(strkPool.poolContract, Amount.parse("100", STRK));
// User's tokens are now earning rewards in the background

// 3. Check position - Show user their earnings
const position = await wallet.getPoolPosition(strkPool.poolContract);
console.log(`Staked: ${position.staked.toFormatted()}`); // "100 STRK"
console.log(`Rewards earned: ${position.rewards.toFormatted()}`); // "2.5 STRK" (example)

// 4. Add more later - User can increase their stake anytime
await wallet.addToPool(strkPool.poolContract, Amount.parse("50", STRK));
// Now staking 150 STRK total, earning more rewards

// 5. Claim rewards - User withdraws their earned rewards
await wallet.claimPoolRewards(strkPool.poolContract);
// Rewards are sent to user's wallet, can be spent or re-staked

// 6. Exit (two-step) - User wants to withdraw some tokens
await wallet.exitPoolIntent(strkPool.poolContract, Amount.parse("25", STRK));
// ... wait for exit window (cooldown period) ...
await wallet.exitPool(strkPool.poolContract);
// 25 STRK returned to user's wallet

Monitoring Rewards

Show users their earnings and help them claim rewards:
async function checkRewards(wallet, poolAddress) {
  const position = await wallet.getPoolPosition(poolAddress);
  
  if (!position) {
    console.log("Not a member of this pool");
    return;
  }
  
  if (position.rewards.isZero()) {
    console.log("No rewards available yet");
    return;
  }
  
  // Show user their earnings
  console.log(`Available rewards: ${position.rewards.toFormatted()}`);
  console.log(`Total position: ${position.total.toFormatted()}`); // Staked + rewards
  
  // Auto-claim if above threshold (optional)
  if (position.rewards.gt(Amount.parse("1", STRK))) {
    await wallet.claimPoolRewards(poolAddress);
    console.log("Rewards claimed! Tokens sent to your wallet.");
  }
}
User experience: Users can see their earnings growing in real-time, claim rewards whenever they want, and watch their total position value increase over time.

User Benefits Summary

What users can do with staking in your app:
  • 💰 Earn passive income - Stake tokens and earn rewards automatically, like interest on a savings account
  • 📈 Grow their holdings - Rewards accumulate over time, increasing their total token balance
  • 🔄 Flexible management - Add more tokens anytime, claim rewards when ready, exit when needed
  • 🏦 Support the network - Help secure Starknet while earning rewards
  • ⏱️ Set and forget - Once staked, tokens earn rewards in the background without daily management
Real-world use cases:
  • Savings feature - Let users “save” their tokens and earn interest
  • Investment tool - Enable users to grow their crypto holdings over time
  • Rewards program - Users can stake tokens to earn additional rewards
  • Long-term holding - Encourage users to hold tokens by offering staking rewards

Best Practices

  1. Always check pool membership before calling addToPool
  2. Monitor rewards regularly and show users their earnings
  3. Help users understand exit windows before initiating exit
  4. Use transaction builder to batch staking with other operations
  5. Override staking contract only if needed (custom deployments, forks, or private environments)
  6. Display earnings clearly - Show users their staked amount, earned rewards, and total position value

Troubleshooting

”Staking contract address is wrong in the config.”

If you override staking.contract, ensure it matches the selected chain and pool. If unsure, remove the override and use the built-in preset for your chain.

Pool Not Found

Ensure you’re using the correct pool contract address. Use getStakerPools() to discover available pools.

Next Steps