Skip to content

Staking NFTs

Staking an XPowerNft locks it in the NftTreasury and mints an APowerNft — a staking receipt that earns continuous APOW rewards proportional to your staked denomination, level, and duration.

Prerequisites

  • One or more XPowerNfts in your wallet (see Depositing XPOW)
  • AVAX for gas (~0.005–0.01 per stake transaction)
  • The XPower app or direct contract access

Step-by-Step

1. Approve the NftTreasury to Handle Your NFTs

The NftTreasury needs permission to take custody of your XPowerNfts. Since XPowerNft is ERC-1155, use setApprovalForAll:

XPowerNft.setApprovalForAll(nftTreasuryAddress, true)

This grants the NftTreasury blanket approval to transfer all your XPowerNfts. You only need to call this once — subsequent stakes reuse the same approval.

Gas cost: ~46,000.

Revoking Approval

If you later want to revoke the treasury's access, call setApprovalForAll(nftTreasuryAddress, false). Note that this does not affect already-staked NFTs — those are already held by the treasury and must be unstaked separately.

2. Stake Your NFTs

NftTreasury.stake(yourAddress, nftId, amount)

Where:

  • yourAddress is the owner of the XPowerNfts (must be msg.sender)
  • nftId is the token ID of the XPowerNft you're staking (e.g., 202603)
  • amount is how many NFTs of this ID to stake

Example: staking 2 level-6 NFTs minted in 2026:

NftTreasury.stake("0xYour...", 202606, 2)

Gas cost: ~150,000–250,000 (includes rate refresh, APowerNft mint, and storage updates).

3. Receive Your APowerNft

After the transaction confirms, you receive an APowerNft — an ERC-1155 staking receipt with the same token ID and amount. This APowerNft:

  • Represents your staked position in the NftTreasury
  • Accumulates age (time-weighted presence) used to calculate your APB bonus
  • Is fully transferable (though age resets on transfer)
  • Can be partially unstaked — you don't need to withdraw everything at once

4. Verify Your Stake

  1. Check your XPowerNft balance — it should decrease by the staked amount
  2. Check your APowerNft balance — it should increase by the staked amount for the same nftId
  3. Verify on-chain: the Stake event emitted by NftTreasury confirms the stake details
  4. Your rewards begin accruing immediately from the block the stake is confirmed

What Happens Internally

When you call NftTreasury.stake(account, nftId, amount), a coordinated sequence executes across multiple contracts:

1. XPowerNft Custody

The NftTreasury calls XPowerNft.safeTransferFrom(account, nftTreasuryAddress, nftId, amount, ""). Your XPowerNft moves into the treasury's escrow balance. The treasury now holds the underlying claim on the locked XPOW.

2. APowerNft Minting

The NftTreasury calls APowerNft.mint(account, nftId, amount, age=0). A new APowerNft is created in your wallet with:

  • Same ID and amount as the deposited XPowerNft — the 1:1 correspondence is preserved
  • Age initialized to 0 — your holding period starts now
  • Share totals updated — the _shares[nftId] accumulator increases by amount * denomination, feeding into the dynamic rate scalar

Only the NftTreasury can mint APowerNfts (enforced via onlyOwner). Users cannot create them directly.

3. Rate Refresh

The NftTreasury calls MoeTreasury.refreshRates(false). This recomputes the dynamic scalar for every active level, redistributing the reward budget based on the new share distribution:

scalar=f(sharestotalShares)

If your staked level was under-supplied, the scalar increases, boosting rewards for all stakers at that level. If over-supplied, the scalar decreases. The total reward budget is redistributed — it does not increase or decrease.

4. Event and State

The NftTreasury emits the Stake(account, nftId, amount) event. Storage is updated:

  • NftTreasury's XPowerNft balance increases
  • Your APowerNft balance increases
  • _shares[nftId] in MoeTreasury reflects the new stake
  • Dynamic scalars for all levels are refreshed

Partial Staking

You don't need to stake all your XPowerNfts of a given ID. If you hold 10 level-3 NFTs and only want to stake 3:

NftTreasury.stake(yourAddress, 202603, 3)

The remaining 7 XPowerNfts stay in your wallet, fully transferable. You can stake them later, or leave them unstaked.

Conversely, you can add to an existing staked position. If you already have APowerNfts for ID 202603 and stake more, the new APowerNfts are minted with fresh age=0 while the existing ones retain their accumulated age.

Rate and Reward Context

Staking is the gateway to rewards. Once staked, your APowerNft begins accumulating:

  • APR — a level-based base rate determined by a configurable polynomial
  • APB — an age-based bonus that increases with your holding duration
  • Dynamic scalar — adjusts your effective rate based on how popular your level is

The reward formula combines these factors:

R=(APR+APB)×t×D×1018106×C

Where t is your staked duration in seconds, D=10level is your denomination, and C is the CENTURY constant (36,525 days). See Claiming APOW for how to collect these rewards.

Gas Summary

ActionApprox. GasNotes
setApprovalForAll (one-time)~46,000Only needed once per wallet
stake (single NFT)~150,000–250,000Includes rate refresh
stake (batch, if supported)Higher per NFTAmortizes rate refresh overhead

Next Steps

Once your NFTs are staked: