contract StonezMigrator is Ownable {
  /**
   * @notice The token is not migratable. This should only happen for
   * tokens that are outside the migratable range, which means that the token
   * was minted *after* the migration was enabled.
   */
  error Unmigratable();

  /**
   * @notice The migration has been paused.
   */
  error Paused();

  /**
   * @notice The address used to burn MetaStonez NFTs.
   */
  address constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;

  /**
   * @notice The old MetaStonez contract.
   */
  IERC721 public metaStonezV1;

  /**
   * @notice The max Genesis token ID that can be migrated (non-inclusive).
   */
  uint256 public GENESIS_CUTOFF;
  /**
   * @notice The new Genesis contract.
   */
  IDelegatedMintable public genesis;

  /**
   * @notice The max Origins token ID that can be migrated (non-inclusive).
   */
  uint256 public ORIGINS_CUTOFF;
  /**
   * @notice The new Origins contract.
   */
  IDelegatedMintable public origins;

  /**
   * @notice Whether or not the migration has been paused.
   */
  bool public paused;

  constructor(
    address _metaStonezV1,
    address _genesis,
    uint256 _genesisCutoff,
    address _origins,
    uint256 _originsCutoff
  ) {
    // Old MetaStonez contract
    metaStonezV1 = IERC721(_metaStonezV1);

    // New Genesis parameters
    genesis = IDelegatedMintable(_genesis);
    GENESIS_CUTOFF = _genesisCutoff;

    // New Origins parameters
    origins = IDelegatedMintable(_origins);
    ORIGINS_CUTOFF = _originsCutoff;
  }

  /**
   * @notice Migrate old MetaStonez tokens into the new contract.
   * Genesis and Origins are split off into separate contracts.
   * Migration gives you another Origins for free per migrated token.
   * Note: To use this function you must approve the token to be burned first.
   */
  function migrate(uint256[] calldata tokenIds) public {
    if (paused) {
      revert Paused();
    }

    for (uint256 i = 0; i < tokenIds.length; i++) {
      uint256 tokenId = tokenIds[i];

      if (tokenId >= ORIGINS_CUTOFF) {
        revert Unmigratable();
      }

      // burn the old one
      metaStonezV1.transferFrom(msg.sender, BURN_ADDRESS, tokenId);

      // mint a new one
      if (tokenId < GENESIS_CUTOFF) {
        genesis.mint(msg.sender, tokenId);
      } else {
        origins.mint(msg.sender, origins.totalSupply());
      }

      // mint an additional origins
      origins.mint(msg.sender, origins.totalSupply());
    }
  }

  /**
   * @notice Pause or unpause the contract.
   */
  function setPaused(bool pause) public onlyOwner {
    paused = pause;
  }
}
function migrate(uint256[] calldata tokenIds) public {
  if (paused) {
    revert Paused();
  }

  for (uint256 i = 0; i < tokenIds.length; i++) {
    uint256 tokenId = tokenIds[i];

    if (tokenId >= ORIGINS_CUTOFF) {
      revert Unmigratable();
    }

    // burn the old one
    metaStonezV1.transferFrom(msg.sender, BURN_ADDRESS, tokenId);

    // mint a new one
    if (tokenId < GENESIS_CUTOFF) {
      genesis.mint(msg.sender, tokenId);
    } else {
      origins.mint(msg.sender, origins.totalSupply());
    }

    // mint an additional origins
    origins.mint(msg.sender, origins.totalSupply());
  }
}

/**
 * @notice Migrate old MetaStonez tokens into the new contract.
 * Genesis and Origins are split off into separate contracts.
 * Migration gives you another Origins for free per migrated token.
 * Note: To use this function you must approve the token to be burned first.
 */