Looking for some feedback on custom multi-transfer function

Hey All,

I'm working through a smart contract and have been reading up on security best practices, I wanted to post a particular function I'm working on here to get some feedback.

I am aware that the way I am generating the random function is 'pseudo' random, however due to the fact that I am going to be launching this contract on layer 2 protocols, I do not believe I have VRF as an option. The function is also onlyOwner, so I believe there is no risk here as no one can really guess the exact block.timestamp as to when I am going to manually call the function.

What I am most concerned about is that this code is sustainable and secure (i.e. not open to re-entry attacks, running out of gas mid function etc.)

Thanks in advance!

function transferERC20(address _token, address to, uint256 amount) public onlyOwner {
    require(amount <= IERC20(_token).balanceOf(address(this)), "Insufficient balance");
    IERC20(_token).safeTransfer(to, amount);
}

event Winner(address indexed _winner, uint256 _amount);

function giveaway() public onlyOwner nonReentrant {
    require(countSpecialIDs > 0, "Special IDs have not been uploaded");
    require(devWallets.length > 0, "No dev wallets specified");
    require(treasuryWallets.length > 0, "No treasury wallets specified");

    uint256 erc20balance = nodeTokenBalance();
    require((erc20balance / 10000) * 10000 == erc20balance, "Node token balance too small");

    uint256 winTokenId = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, block.difficulty))) % supply.current();
    address winnerAddress = ownerOf(winTokenId);

    // Prevent burnaddress winner
        while (burnAddresses[winnerAddress]) {
           winTokenId = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, block.difficulty))) % supply.current();
           winnerAddress = ownerOf(winTokenId);
        }

    uint256 devAmount = erc20balance * devFee / 10000;
    uint256 treasuryAmount = (erc20balance * compoundFee / 10000);
    uint256 winnerAmount = (erc20balance * winnerFee / 10000);
    if (specialIDs[winTokenId]) {
        winnerAmount = erc20balance * (winnerFee + specialFee) / 10000;
        treasuryAmount = erc20balance * (compoundFee - specialFee) / 10000;
    }
    require(devAmount > 0, "Dev fee could not be calculated");
    require(treasuryAmount > 0, "Treasury fee could not be calculated");
    require(winnerAmount > 0, "Winner fee could not be calculated");

    for (uint256 i = 0; i < treasuryWallets.length; i++) {
        transferERC20(nodeToken, address(treasuryWallets[i]), treasuryAmount / treasuryWallets.length);
    }
    for (uint256 i = 0; i < devWallets.length; i++) {
        transferERC20(address(nodeToken), address(devWallets[i]), devAmount / devWallets.length);
    }
    transferERC20(nodeToken, winnerAddress, winnerAmount);
    emit Winner(winnerAddress, winnerAmount);
}

Emmmm, how to trust the owner does not manipulate the final winTokenId?

Is there an alternative way to get random number on layer 2 chains?