Skip to content

Unstaking

Unstaking returns your XPowerNfts from the NftTreasury in exchange for burning your APowerNft staking receipts. Rewards stop accruing for the unstaked portion.

Prerequisites

  • One or more APowerNfts in your wallet (see Staking NFTs)
  • AVAX for gas (~0.005–0.02 per unstake, including the required prior claim)
  • Any pending rewards must be claimed first

Step-by-Step

1. Claim Pending Rewards First

The NftTreasury requires you to claim all pending rewards before unstaking. Attempting to unstake with unclaimed rewards will revert. This ensures you don't forfeit accrued APOW.

MoeTreasury.claim(yourAddress, nftId, nonce)

See Claiming APOW for the full claiming procedure. After claiming, your _claimed accumulator is up to date and rewardOf() will reflect zero pending rewards.

Gas cost: ~150,000–300,000.

2. Unstake Your NFTs

NftTreasury.unstake(yourAddress, nftId, amount)

Where:

  • yourAddress is the APowerNft holder (must be msg.sender)
  • nftId is the token ID of your staked position (e.g., 202603)
  • amount is how many APowerNfts of this ID to unstake

Example: unstaking 1 level-6 staking receipt:

NftTreasury.unstake("0xYour...", 202606, 1)

Gas cost: ~150,000–250,000 (includes APowerNft burn, XPowerNft return, and rate refresh).

3. APowerNft Is Burned, XPowerNft Returned

After the transaction confirms:

  1. The specified amount of APowerNfts is burned — they no longer exist
  2. The equivalent amount of XPowerNfts is transferred from the NftTreasury back to your wallet
  3. Your APowerNft balance decreases, your XPowerNft balance increases

The returned XPowerNft is the same original token (same ID, same mint year, same maturity date). Staking does not extend the lock period — your NFT matures on the same schedule regardless of how long it was staked.

4. Verify the Unstake

  1. Check your XPowerNft balance — it should increase by amount for the given nftId
  2. Check your APowerNft balance — it should decrease by amount
  3. View the Unstake event in the transaction logs
  4. Confirm no pending rewards remain — if rewardOf() still shows a balance, claim it in a separate transaction

Age Reset

When you unstake, the age accumulator for that position is proportionally reduced:

  • Full unstake: The entire age is cleared. If you re-stake later, age starts from zero.
  • Partial unstake: The remaining staked position retains a proportionally adjusted age. For example, if you unstake 3 out of 10 APowerNfts at a given ID, the remaining 7 keep 70% of the accumulated age.

This age reset is critical — APR rewards are based purely on the level (which doesn't change), but APB rewards depend on age. Unstaking resets the APB clock, so you'll earn at a lower rate if you re-stake the same NFTs later.

Partial Unstaking

You can unstake a portion of your position without touching the rest:

NftTreasury.unstake(yourAddress, nftId, partialAmount)

Where partialAmount < totalStakedAmount.

When you partially unstake:

  1. partialAmount APowerNfts are burned and returned as XPowerNfts
  2. The remaining APowerNfts keep their proportionally adjusted age
  3. The remaining XPowerNfts stay in the treasury
  4. Rates are refreshed to reflect the new share distribution

Example: you staked 10 level-3 NFTs. You want 3 back:

NftTreasury.unstake("0xYour...", 202603, 3)

You receive 3 XPowerNfts back, 7 APowerNfts remain staked with 70% of the accumulated age, and rewards continue accruing on the remaining position.

Automatic Rate Refresh

Every unstake triggers MoeTreasury.refreshRates(false), which recomputes the dynamic scalar for all levels. If your level was over-supplied, unstaking may improve rates for the remaining stakers at that level.

Post-Unstake Options

Once you have your XPowerNfts back, you can:

  • Re-stake immediately — call NftTreasury.stake() again (age resets to zero, APB bonus lost)
  • Transfer or sell — XPowerNfts are fully transferable via standard ERC-1155
  • Redeem for XPOW — if the NFT has reached maturity, call XPowerNft.burn() to release the underlying XPOW
  • Upgrade — if you have 1,000+ NFTs at the same level, upgrade to the next tier (see Upgrading NFTs)

What Happens Internally

When you call NftTreasury.unstake(account, nftId, amount), the following sequence executes:

1. Authorization

The contract verifies account == msg.sender — you can only unstake your own position. Attempting to unstake another address's NFTs reverts with UnauthorizedAccount.

2. Reward Check

The NftTreasury calls MoeTreasury.claimable(account, nftId) to check for unclaimed rewards. If the return value is non-zero, the unstake reverts with PendingRewards — you must call claim() first. This prevents stakers from accidentally forfeiting earned APOW.

3. APowerNft Burn

The NftTreasury calls APowerNft.burn(account, nftId, amount), which:

  • Reduces your APowerNft balance by amount
  • Proportionally adjusts the _age accumulator — the age reduction formula preserves the average staking duration for the remaining position
  • Updates _shares[nftId] in MoeTreasury — the denominator decreases by amount * denomination

Only the NftTreasury can burn APowerNfts (enforced via onlyOwner on the APowerNft contract).

4. XPowerNft Return

The NftTreasury calls XPowerNft.safeTransferFrom(nftTreasuryAddress, account, nftId, amount, ""). This moves the original XPowerNft from the treasury's escrow back to your wallet. The 1:1 correspondence between deposited and returned NFTs is maintained.

5. Rate Refresh

The NftTreasury calls MoeTreasury.refreshRates(false). The share distribution is recomputed, and the dynamic scalar for each level is updated. Since your shares have decreased, the scalars may shift — potentially benefiting remaining stakers at your level (if it was over-supplied) or reducing the bonus (if it becomes under-supplied relative to the new distribution).

6. Event Emission

The Unstake(account, nftId, amount) event is emitted with the unstake details. This event is the authoritative on-chain record of the unstake and can be used for off-chain accounting and portfolio tracking.

Gas Summary

ActionApprox. GasNotes
claim (prerequisite)~150,000–300,000Required before unstaking
unstake (full or partial)~150,000–250,000Includes burn, transfer, and rate refresh
Total to exit~300,000–550,000Claim + unstake combined