LOW severity: Queued withdrawals should use an external protocol-owned unlocker to prevent `BunniHub` earning referral rewards. **Description:** When the am-AMM is enabled with an active manager, withdrawals must be queued:

The caller's Bunni tokens are transferred to the `BunniHub`, where they are escrowed until the `WITHDRAW_DELAY` has passed:

However, this overlooks the fact that `BunniToken` inherits `ERC20Referrer`...

Vulnerable code:
function withdraw(HubStorage storage s, Env calldata env, IBunniHub.WithdrawParams calldata params)
        external
        returns (uint256 amount0, uint256 amount1)
    {
        /// -----------------------------------------------------------------------
        /// Validation
        /// -----------------------------------------------------------------------

        if (!params.useQueuedWithdrawal && params.shares == 0) revert BunniHub__ZeroInput();

        PoolId poolId = params.poolKey.toId();
        PoolState memory state = getPoolState(s, poolId);
        IBunniHook hook = IBunniHook(address(params.poolKey.hooks));

        IAmAmm.Bid memory topBid = hook.getTopBidWrite(poolId);
@>      if (hook.getAmAmmEnabled(poolId) && topBid.manager != address(0) && !params.useQueuedWithdrawal) {
            revert BunniHub__NeedToUseQueuedWithdrawal();
    }

function queueWithdraw(HubStorage storage s, IBunniHub.QueueWithdrawParams calldata params) external {
        /// -----------------------------------------------------------------------
        /// Validation
        /// -----------------------------------------------------------------------

        PoolId id = params.poolKey.toId();
        IBunniToken bunniToken = _getBunniTokenOfPool(s, id);
        if (address(bunniToken) == address(0)) revert BunniHub__BunniTokenNotInitialized();

        /// -----------------------------------------------------------------------
        /// State updates
        /// -----------------------------------------------------------------------

        address msgSender = LibMulticaller.senderOrSigner();
        QueuedWithdrawal memory queued = s.queuedWithdrawals[id][msgSender];

        // update queued withdrawal
        // use unchecked to get unlockTimestamp to overflow back to 0 if overflow occurs
        // which is fine since we only care about relative time
        uint56 newUnlockTimestamp;
        unchecked {
            newUnlockTimestamp = uint56(block.timestamp) + WITHDRAW_DELAY;
        }
        if (queued.shareAmount != 0) {
            // requeue expired queued withdrawal
            if (queued.unlockTimestamp + WITHDRAW_GRACE_PERIOD >= block.timestamp) {
                revert BunniHub__NoExpiredWithdrawal();
            }
            s.queuedWithdrawals[id][msgSender].unlockTimestamp = newUnlockTimestamp;
        } else {
            // create new queued withdrawal
            if (params.shares == 0) revert BunniHub__ZeroInput();
            s.queuedWithdrawals[id][msgSender] =
                QueuedWithdrawal({shareAmount: params.shares, unlockTimestamp: newUnlockTimestamp});
        }

        /// -----------------------------------------------------------------------
        /// External calls
        /// -----------------------------------------------------------------------

        if (queued.shareAmount == 0) {
            // transfer shares from msgSender to address(this)
@>          bunniToken.transferFrom(msgSender, address(this), params.shares);
        }

        emit IBunniHub.QueueWithdraw(msgSender, id, params.shares);
    }