abstract contract Recoverable is ManagedIdentity, Ownable {
    using ERC20Wrapper for IWrappedERC20;

    /**
     * Extract ERC20 tokens which were accidentally sent to the contract to a list of accounts.
     * Warning: this function should be overriden for contracts which are supposed to hold ERC20 tokens
     * so that the extraction is limited to only amounts sent accidentally.
     * @dev Reverts if the sender is not the contract owner.
     * @dev Reverts if `accounts`, `tokens` and `amounts` do not have the same length.
     * @dev Reverts if one of `tokens` is does not implement the ERC20 transfer function.
     * @dev Reverts if one of the ERC20 transfers fail for any reason.
     * @param accounts the list of accounts to transfer the tokens to.
     * @param tokens the list of ERC20 token addresses.
     * @param amounts the list of token amounts to transfer.
     */
    function recoverERC20s(
        address[] calldata accounts,
        address[] calldata tokens,
        uint256[] calldata amounts
    ) external virtual {
        _requireOwnership(_msgSender());
        uint256 length = accounts.length;
        require(length == tokens.length && length == amounts.length, "Recov: inconsistent arrays");
        for (uint256 i = 0; i != length; ++i) {
            IWrappedERC20(tokens[i]).wrappedTransfer(accounts[i], amounts[i]);
        }
    }

    /**
     * Extract ERC721 tokens which were accidentally sent to the contract to a list of accounts.
     * Warning: this function should be overriden for contracts which are supposed to hold ERC721 tokens
     * so that the extraction is limited to only tokens sent accidentally.
     * @dev Reverts if the sender is not the contract owner.
     * @dev Reverts if `accounts`, `contracts` and `amounts` do not have the same length.
     * @dev Reverts if one of `contracts` is does not implement the ERC721 transferFrom function.
     * @dev Reverts if one of the ERC721 transfers fail for any reason.
     * @param accounts the list of accounts to transfer the tokens to.
     * @param contracts the list of ERC721 contract addresses.
     * @param tokenIds the list of token ids to transfer.
     */
    function recoverERC721s(
        address[] calldata accounts,
        address[] calldata contracts,
        uint256[] calldata tokenIds
    ) external virtual {
        _requireOwnership(_msgSender());
        uint256 length = accounts.length;
        require(length == contracts.length && length == tokenIds.length, "Recov: inconsistent arrays");
        for (uint256 i = 0; i != length; ++i) {
            IRecoverableERC721(contracts[i]).transferFrom(address(this), accounts[i], tokenIds[i]);
        }
    }
}
function recoverERC20s(
    address[] calldata accounts,
    address[] calldata tokens,
    uint256[] calldata amounts
) external virtual {
    _requireOwnership(_msgSender());
    uint256 length = accounts.length;
    require(length == tokens.length && length == amounts.length, "Recov: inconsistent arrays");
    for (uint256 i = 0; i != length; ++i) {
        IWrappedERC20(tokens[i]).wrappedTransfer(accounts[i], amounts[i]);
    }
}

/**
 * Extract ERC20 tokens which were accidentally sent to the contract to a list of accounts.
 * Warning: this function should be overriden for contracts which are supposed to hold ERC20 tokens
 * so that the extraction is limited to only amounts sent accidentally.
 * @dev Reverts if the sender is not the contract owner.
 * @dev Reverts if `accounts`, `tokens` and `amounts` do not have the same length.
 * @dev Reverts if one of `tokens` is does not implement the ERC20 transfer function.
 * @dev Reverts if one of the ERC20 transfers fail for any reason.
 * @param accounts the list of accounts to transfer the tokens to.
 * @param tokens the list of ERC20 token addresses.
 * @param amounts the list of token amounts to transfer.
 */