PeriodicPrizeStrategy - unbounded external tokens linked list may be used to force a gas DoS
The size of the linked list of ERC20/ERC721 token awards is not limited. This fact may be exploited by an administrative account by adding an excessive number of external token addresses.\\nThe winning user might want to claim their win by calling `completeAward()` which fails in one of the `_distribute() -> _awardAllExternalTokens() -> _awardExternalErc20s/_awardExternalErc721s` while loops if too many token addresses are configured and gas consumption hits the block gas limit (or it just gets too expensive for the user to call).\\nNote: an admin can recover from this situation by removing items from the list.\\n```\\n/// @notice Adds an external ERC20 token type 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/// @param \\_externalErc20 The address of an ERC20 token to be awarded\\nfunction addExternalErc20Award(address \\_externalErc20) external onlyOwnerOrListener {\\n  \\_addExternalErc20Award(\\_externalErc20);\\n}\\n\\nfunction \\_addExternalErc20Award(address \\_externalErc20) internal {\\n  require(prizePool.canAwardExternal(\\_externalErc20), "PeriodicPrizeStrategy/cannot-award-external");\\n  externalErc20s.addAddress(\\_externalErc20);\\n  emit ExternalErc20AwardAdded(\\_externalErc20);\\n}\\n```\\n\\n```\\n/// @param newAddress The address to shift to the front of the list\\nfunction addAddress(Mapping storage self, address newAddress) internal {\\n  require(newAddress != SENTINEL && newAddress != address(0), "Invalid address");\\n  require(self.addressMap[newAddress] == address(0), "Already added");\\n  self.addressMap[newAddress] = self.addressMap[SENTINEL];\\n  self.addressMap[SENTINEL] = newAddress;\\n  self.count = self.count + 1;\\n}\\n```\\n\\nawarding the tokens loops through the linked list of configured 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
Limit the number of tokens an admin can add. Consider implementing an interface that allows the user to claim tokens one-by-one or in user-configured batches.
```\\n/// @notice Adds an external ERC20 token type 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/// @param \\_externalErc20 The address of an ERC20 token to be awarded\\nfunction addExternalErc20Award(address \\_externalErc20) external onlyOwnerOrListener {\\n  \\_addExternalErc20Award(\\_externalErc20);\\n}\\n\\nfunction \\_addExternalErc20Award(address \\_externalErc20) internal {\\n  require(prizePool.canAwardExternal(\\_externalErc20), "PeriodicPrizeStrategy/cannot-award-external");\\n  externalErc20s.addAddress(\\_externalErc20);\\n  emit ExternalErc20AwardAdded(\\_externalErc20);\\n}\\n```\\n