PeriodicPriceStrategy - addExternalErc721Award duplicate or invalid tokenIds may block award phase
The prize-strategy owner (or a listener) can add `ERC721` token awards by calling `addExternalErc721Award` providing the `ERC721` token address and a list of `tokenIds` owned by the prizePool.\\nThe method does not check if duplicate `tokenIds` or `tokenIds` that are not owned by the contract are provided. This may cause an exception when `_awardExternalErc721s` calls `prizePool.awardExternalERC721` to transfer an invalid or previously transferred token, blocking the award phase.\\nNote: An admin can recover from this situation by removing and re-adding the `ERC721` token from the awards list.\\nadding `tokenIds`\\n```\\n/// @notice Adds an external ERC721 token as an additional prize that can be awarded\\n/// @dev Only the Prize-Strategy owner/creator can assign external tokens,\\n/// and they must be approved by the Prize-Pool\\n/// NOTE: The NFT must already be owned by the Prize-Pool\\n/// @param \\_externalErc721 The address of an ERC721 token to be awarded\\n/// @param \\_tokenIds An array of token IDs of the ERC721 to be awarded\\nfunction addExternalErc721Award(address \\_externalErc721, uint256[] calldata \\_tokenIds) external onlyOwnerOrListener {\\n  // require(\\_externalErc721.isContract(), "PeriodicPrizeStrategy/external-erc721-not-contract");\\n  require(prizePool.canAwardExternal(\\_externalErc721), "PeriodicPrizeStrategy/cannot-award-external");\\n \\n  if (!externalErc721s.contains(\\_externalErc721)) {\\n    externalErc721s.addAddress(\\_externalErc721);\\n  }\\n\\n  for (uint256 i = 0; i < \\_tokenIds.length; i++) {\\n    uint256 tokenId = \\_tokenIds[i];\\n    require(IERC721(\\_externalErc721).ownerOf(tokenId) == address(prizePool), "PeriodicPrizeStrategy/unavailable-token");\\n    externalErc721TokenIds[\\_externalErc721].push(tokenId);\\n  }\\n\\n  emit ExternalErc721AwardAdded(\\_externalErc721, \\_tokenIds);\\n}\\n```\\n\\nawarding tokens\\n```\\n/// @notice Awards all external ERC721 tokens to the given user.\\n/// The external tokens must be held by the PrizePool contract.\\n/// @dev The list of ERC721s is reset after every award\\n/// @param winner The user to transfer the tokens to\\nfunction \\_awardExternalErc721s(address winner) internal {\\n  address currentToken = externalErc721s.start();\\n  while (currentToken != address(0) && currentToken != externalErc721s.end()) {\\n    uint256 balance = IERC721(currentToken).balanceOf(address(prizePool));\\n    if (balance > 0) {\\n      prizePool.awardExternalERC721(winner, currentToken, externalErc721TokenIds[currentToken]);\\n      delete externalErc721TokenIds[currentToken];\\n    }\\n    currentToken = externalErc721s.next(currentToken);\\n  }\\n  externalErc721s.clearAll();\\n}\\n```\\n\\ntransferring the tokens\\n```\\n/// @notice Called by the prize strategy to award external ERC721 prizes\\n/// @dev Used to award any arbitrary NFTs held by the Prize Pool\\n/// @param to The address of the winner that receives the award\\n/// @param externalToken The address of the external NFT token being awarded\\n/// @param tokenIds An array of NFT Token IDs to be transferred\\nfunction awardExternalERC721(\\n  address to,\\n  address externalToken,\\n  uint256[] calldata tokenIds\\n)\\n  external override\\n  onlyPrizeStrategy\\n{\\n  require(\\_canAwardExternal(externalToken), "PrizePool/invalid-external-token");\\n\\n  if (tokenIds.length == 0) {\\n    return;\\n  }\\n\\n  for (uint256 i = 0; i < tokenIds.length; i++) {\\n    IERC721(externalToken).transferFrom(address(this), to, tokenIds[i]);\\n  }\\n\\n  emit AwardedExternalERC721(to, externalToken, tokenIds);\\n}\\n```\\n
Ensure that no duplicate token-ids were provided or skip over token-ids that are not owned by prize-pool (anymore).
```\\n/// @notice Adds an external ERC721 token as an additional prize that can be awarded\\n/// @dev Only the Prize-Strategy owner/creator can assign external tokens,\\n/// and they must be approved by the Prize-Pool\\n/// NOTE: The NFT must already be owned by the Prize-Pool\\n/// @param \\_externalErc721 The address of an ERC721 token to be awarded\\n/// @param \\_tokenIds An array of token IDs of the ERC721 to be awarded\\nfunction addExternalErc721Award(address \\_externalErc721, uint256[] calldata \\_tokenIds) external onlyOwnerOrListener {\\n  // require(\\_externalErc721.isContract(), "PeriodicPrizeStrategy/external-erc721-not-contract");\\n  require(prizePool.canAwardExternal(\\_externalErc721), "PeriodicPrizeStrategy/cannot-award-external");\\n \\n  if (!externalErc721s.contains(\\_externalErc721)) {\\n    externalErc721s.addAddress(\\_externalErc721);\\n  }\\n\\n  for (uint256 i = 0; i < \\_tokenIds.length; i++) {\\n    uint256 tokenId = \\_tokenIds[i];\\n    require(IERC721(\\_externalErc721).ownerOf(tokenId) == address(prizePool), "PeriodicPrizeStrategy/unavailable-token");\\n    externalErc721TokenIds[\\_externalErc721].push(tokenId);\\n  }\\n\\n  emit ExternalErc721AwardAdded(\\_externalErc721, \\_tokenIds);\\n}\\n```\\n