Skip to content

Minting and Upgrading

XPowerNfts are created by depositing XPOW tokens and can be consolidated into higher-tier NFTs through the upgrade mechanism. This page covers the complete lifecycle of NFT creation, destruction, and tier progression.

Minting

Minting converts XPOW into an XPowerNft. The user must first approve the XPowerNft contract to spend their XPOW via the ERC20 approve mechanism.

Single Mint

solidity
function mint(address account, uint256 level, uint256 amount) external

Parameters:

  • account — recipient of the minted NFTs (must be msg.sender or an approved minter)
  • level — the tier level (must be a multiple of 3, 0 ≤ level < 100)
  • amount — number of NFTs to mint at this level

Process:

  1. Authorization: require(account == msg.sender || approvedMint(account, msg.sender))
  2. XPOW deposit: XPower.transferFrom(account, this, amount × 10^level × 10^18)
  3. NFT mint: _mint(account, idBy(year(), level), amount, "")

Example: Minting 5 level-3 NFTs in 2026:

  • XPOW cost: 5 × 10^3 = 5,000 XPOW transferred from user
  • NFTs received: 5 × id=202603 (XPowerNft at level 3, mint year 2026)
  • Each NFT represents 1,000 XPOW

Batch Mint

solidity
function mintBatch(address account, uint256[] memory levels, uint256[] memory amounts) external

Mints multiple levels in a single transaction. The total XPOW cost is the sum of amounts[i] × 10^levels[i], transferred in one transferFrom call.

Example: Minting both level-0 and level-6 NFTs in one transaction:

  • levels = [0, 6], amounts = [100, 2]
  • XPOW cost: 100 × 1 + 2 × 1,000,000 = 2,000,100 XPOW
  • NFTs received: 100 × id=202600 + 2 × id=202606

Batch minting saves gas by consolidating multiple mints into a single XPOW transfer.

Minting Approvals

Users can delegate minting authority to another address:

solidity
function approveMint(address operator, bool approved) external

This is separate from ERC1155 transfer approvals. A use case: a contract or relay service that mints NFTs on a user's behalf without needing custody of their XPOW.

Burning and Redemption

Burning destroys an XPowerNft and releases its underlying XPOW back to the holder — but only if the NFT has reached maturity.

Single Burn

solidity
function burn(address account, uint256 id, uint256 amount) public override

Process:

  1. Maturity check: require(yearOf(id) + 2^(levelOf(id)/3) - 1 <= year())
  2. ERC1155 burn: _burn(account, id, amount)
  3. XPOW release: XPower.transfer(account, amount × denominationOf(levelOf(id)) × 10^18)

If the NFT has not yet matured, the transaction reverts with IrredeemableIssue(id).

Example: Burning 3 level-6 NFTs minted in 2026 (matures 2029):

  • In 2030: maturity check passes, 3 × 1,000,000 = 3,000,000 XPOW released
  • In 2028: maturity check fails, transaction reverts

Batch Burn

solidity
function burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) public override

Burns multiple token IDs in a single transaction. All IDs must have passed their respective maturity dates — the function checks all IDs upfront and reverts if any are still immature.

Gas note: Batch burns are particularly efficient for unwinding diversified NFT positions. Instead of n separate transactions (each with its own XPOW transfer), a single batch burn performs one combined XPOW transfer for the total.

Upgrading

Upgrading consolidates lower-level NFTs into higher-level ones at a fixed 1,000:1 ratio. This is the mechanism by which users progress through the tier system without needing additional XPOW.

The Upgrade Ratio

The core invariant:

1,000 NFTs at level 1 NFT at level +3

This preserves XPOW value because:

1,000×10=103×10=10+3

Single Upgrade

solidity
function upgrade(address account, uint256 anno, uint256 level, uint256 amount) external

Parameters:

  • account — owner of the NFTs (must be msg.sender or approved)
  • anno — the year of the source NFTs (must match the NFTs being upgraded)
  • level — target level after upgrade (must be ≥ 3, i.e., a multiple of 3 greater than 0)
  • amount — number of upgraded NFTs to produce at the target level

Process:

  1. Authorization: require(account == msg.sender || approvedUpgrade(account, msg.sender))
  2. Level validation: require(level > 2) (must be at least 3)
  3. Burn source: _burn(account, idBy(anno, level-3), amount × 1000)
  4. Mint target: _mint(account, idBy(anno, level), amount, "")

Example 1: Clean upgrade

  • User holds 3,000 level-3 NFTs (id=202603)
  • Calls upgrade(user, 2026, 6, 3)
  • 3,000 level-3 NFTs are burned
  • 3 level-6 NFTs (id=202606) are minted
  • Total XPOW value unchanged: 3,000×103=3×106=3,000,000

Example 2: Partial upgrade

  • User holds 2,500 level-3 NFTs
  • Calls upgrade(user, 2026, 6, 2)
  • 2,000 level-3 NFTs are burned (amount × 1000 = 2 × 1000)
  • 2 level-6 NFTs are minted
  • Remaining: 500 level-3 NFTs still held by user

Important: Upgrades always use the same year for source and target. You cannot upgrade a 2026 level-3 into a 2027 level-6 — the year parameter is shared, preserving the mint vintage.

Batch Upgrade

solidity
function upgradeBatch(
    address account,
    uint256[] memory annos,
    uint256[][] memory levels,
    uint256[][] memory amounts
) external

Performs multiple upgrades across different years and target levels in a single transaction.

Parameters:

  • annos — array of years, one per upgrade group
  • levels — 2D array: levels[i] is the array of target levels for year annos[i]
  • amounts — 2D array: amounts[i][j] is the amount for target level levels[i][j]

The function processes upgrades group by group: for each year, it burns all source NFTs (at level-3 for each target level, multiplied by 1000) and mints all target NFTs. This uses _burnBatch and _mintBatch internally for gas efficiency.

Upgrade Approvals

Like minting, upgrading supports delegated execution:

solidity
function approveUpgrade(address operator, bool approved) external

A contract or service can be authorized to perform upgrades on a user's behalf. This is separate from minting and transfer approvals — each operation type has its own approval mapping.

Gas Considerations

Minting

  • Base cost: ERC20 transferFrom + ERC1155 _mint per token ID
  • Batch mint reduces XPOW transfers to 1 (instead of 1 per level), saving ~20k–50k gas per additional level

Burning

  • Each ID requires a maturity check (constant-time: yearOf, levelOf, exponentiation)
  • Batch burn combines all XPOW releases into a single transfer, saving gas proportional to the number of IDs

Upgrading

  • Each upgrade burns amount × 1000 source NFTs, meaning gas costs scale linearly with the number of source NFTs
  • Upgrading 1,000 level-3 → 1 level-6 costs roughly the same as burning 1,000 NFTs plus minting 1
  • Upgrading 1,000,000 level-0 → 1 level-18 requires burning 1,000,000 NFTs — gas-prohibitive. Users should mint at intermediate levels when possible.

Optimization Strategies

StrategyGas Saved
Use mintBatch for multi-level deposits~30k per extra level
Use burnBatch for multi-ID redemptions~25k per extra ID
Mint at target level directly instead of upgradingEliminates all upgrade gas
Coalesce multiple upgrades into upgradeBatchAmortizes per-transaction overhead

Error Conditions

ErrorCondition
NonTernaryLevel(level)Level is not a multiple of 3
InvalidLevel(level)Level ≥ 100
InvalidYear(anno)Year ≤ 2020
IrredeemableIssue(id)NFT has not reached maturity
UnauthorizedAccount(account)Caller is not the owner or an approved operator
SelfApproving(operator)User attempted to self-approve

Complete Lifecycle Example

A user with 3,000,000 XPOW in 2026:

Step 1: Mint 3 × level-6 NFTs
  → approve(XPowerNft, 3,000,000)
  → mint(self, 6, 3)
  → Receives 3 × id=202606 (each worth 1M XPOW)

Step 2: Wait until 2029 (maturity year for level 6 = 2026 + 3)
  → burn(self, 202606, 2)
  → 2,000,000 XPOW released, 1 NFT remains

Step 3: Upgrade remaining level-6
  → Cannot — need 1,000 level-6 to upgrade to level-9
  → Burn final NFT: burn(self, 202606, 1)
  → Remaining 1,000,000 XPOW released