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:
yourAddressis the APowerNft holder (must bemsg.sender)nftIdis the token ID of your staked position (e.g.,202603)amountis 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:
- The specified
amountof APowerNfts is burned — they no longer exist - The equivalent
amountof XPowerNfts is transferred from the NftTreasury back to your wallet - 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
- Check your XPowerNft balance — it should increase by
amountfor the givennftId - Check your APowerNft balance — it should decrease by
amount - View the
Unstakeevent in the transaction logs - 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:
partialAmountAPowerNfts are burned and returned as XPowerNfts- The remaining APowerNfts keep their proportionally adjusted age
- The remaining XPowerNfts stay in the treasury
- 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
_ageaccumulator — the age reduction formula preserves the average staking duration for the remaining position - Updates
_shares[nftId]in MoeTreasury — the denominator decreases byamount * 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
| Action | Approx. Gas | Notes |
|---|---|---|
claim (prerequisite) | ~150,000–300,000 | Required before unstaking |
unstake (full or partial) | ~150,000–250,000 | Includes burn, transfer, and rate refresh |
| Total to exit | ~300,000–550,000 | Claim + unstake combined |